Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial
  • Lezione 38 di 112
  • livello avanzato
Indice lezioni

Creare oggetti con Object.create()

Object.create() è l'alternativa per creare oggetti mantenendo la flessibilità degli oggetti JavaScript e aggiungere espressività grazie ai descrittori.
Object.create() è l'alternativa per creare oggetti mantenendo la flessibilità degli oggetti JavaScript e aggiungere espressività grazie ai descrittori.
Link copiato negli appunti

Una alternativa ai metodi visti finora per la creazione di oggetti è quella di fare ricorso al metodo create() dell'oggetto base Object. Questo metodo ci consente sia di mantenere la flessibilità tipica degli oggetti JavaScript sia di aggiungere un ulteriore livello di espressività nella definizione di un oggetto. Ma vediamo come utilizzarlo mediante alcuni esempi.

Nella sua forma minima il metodo create() consente di creare un oggetto specificando il prototipo di riferimento:

var persona = Object.create(null);

In questo esempio abbiamo specificato null come prototipo di riferimento dell'oggetto persona che abbiamo creato. Il risultato è un oggetto senza proprietà né metodi, nemmeno quelli ereditati dall'oggetto base Object, come avremmo ottenuto creando un oggetto vuoto tramite notazione letterale ({}). Per ottenere un oggetto equivalente all'oggetto vuoto dobbiamo scrivere il seguente codice:

var persona = Object.create(Object.prototype);

Con esso specifichiamo di voler creare un oggetto il cui prototipo è il prototipo di Object.

Naturalmente il principio vale per qualsiasi oggetto. Riconsideriamo quindi la nostra definizione dell'oggetto persona definendo opportunamente il suo prototipo:

function persona(nome, cognome) {
	this.nome = (nome || "");
	this.cognome = (cognome ||"");
}
persona.prototype.indirizzo = "";
persona.prototype.email = "";
persona.prototype.mostraNomeCompleto = function() {return this.nome + " " + this.cognome};
persona.prototype.calcolaCodiceFiscale = function() {...};

Abbiamo mantenuto all'interno del costruttore le proprietà che possono essere inizializzate in fase di creazione dell'oggetto e spostato le altre proprietà e metodi sul relativo prototipo.

Possiamo utilizzare il metodo create() per creare un oggetto nel seguente modo:

var marioRossi = Object.create(persona.prototype);

Naturalmente in questo caso abbiamo creato un oggetto senza utilizzare il costruttore e quindi non abbiamo impostato direttamente le due proprietà nome e cognome. Il fatto è che, avendo tenuto le due proprietà fuori dalla definizione del prototipo, esse non sono presenti nella struttura dell'oggetto marioRossi. Possiamo rendercene conto invocando il metodo mostraNomeCompleto(), che restituirà un undefined undefined invece che una stringa vuota come ci aspetteremmo.

L'inizializzazione di un oggetto creato con Object.create() avviene tramite il secondo parametro opzionale del metodo:

var marioRossi = Object.create(
						persona.prototype,
						{
							nome:    { value: "Mario" },
							cognome: { value: "Rossi" }
						});

Questo assegnamento crea un l'oggetto marioRossi basato sul prototipo persona e con le proprietà nome e cognome inizializzate.

A prima vista l'inizializzazione delle proprietà appare una cosa un po' più complessa di quanto probabilmente avremmo immaginato. In realtà l'inizializzazione di un oggetto tramite il metodo create() offre un'opportunità molto interessante nella definizione di oggetti in JavaScript, cioè l'uso di descrittori delle proprietà.

I descrittori delle proprietà

Un descrittore è un oggetto che definisce caratteristiche e modalità di accesso alle proprietà di un oggetto. Possiamo distinguere due tipi di descrittori:

  • data descriptor, un oggetto che definisce una proprietà specificando una serie di caratteristiche predefinite;
  • accessor descriptor, descrive una proprietà tramite una coppia di funzioni di tipo getter e setter.

Consideriamo il seguente esempio:

var marioRossi = Object.create(
	persona.prototype, {
		nome: {
			value        : "Mario",
			writable     : false,
			configurable : false },
		cognome: {
			value        : "Rossi",
			writable     : false,
			configurable : false },
		indirizzo: {
			value        : "",
			writable     : true,
			configurable : true },
		email: {
			value: "", writable: true, configurable: true},
			nomeCompleto: {
				configurable: true,
				get: function() {return this.nome + " " + this.cognome;}
			}
		});

Abbiamo definito ed inizializzato l'oggetto marioRossi specificando che le proprietà nome e cognome non possono essere modificate nè rimosse, mentre le proprietà indirizzo ed email possono essere sia modificate che rimosse o ridefinite. Inoltre abbiamo definito la proprietà in sola lettura nomeCompleto impostando la funzione che fa da getter.

Come possiamo vedere abbiamo un insieme di opzioni per definire come è possibile accedere alle proprietà del nostro oggetto. Andando nello specifico, quelle che seguono sono le opzioni disponibili per la definizione di una proprietà tramite un data descriptor:

writable Booleano che indica se il valore della proprietà può essere modificato
configurable Booleano che indica se il tipo di descrittore può essere modificato e se la proprietà può essere rimossa
enumerable Booleano che indica se la proprietà è accessibile durante un ciclo sulle proprietà dell'oggetto
value Indica il valore della proprietà

Le opzioni disponibili per la definizione di una proprietà tramite un accessor descriptor sono:

configurable Booleano che indica se il tipo di descrittore può essere modificato e se la proprietà può essere rimossa
enumerable Booleano che indica se la proprietà è accessibile durante un ciclo sulle proprietà dell'oggetto
get Funzione senza argomenti invocata quando si accede alla proprietà in lettura
set Funzione chiamata quando si accede alla proprietà in scrittura; il nuovo valore da assegnare alla proprietà viene passato come parametro

Per comprendere come sfruttare gli accessor descriptor, vediamo come definire ad esempio la proprietà email se vogliamo una validazione dei valori che possono essere assegnati:

_email: { value: "", writable: true, configurable: true },
email: {
	get: function() {
			return this._email;
	}, 
	set: function(value) {
		var emailRegExp = /\w+@\w+\.\w{2,4}/i;
		if (emailRegExp.test(value)) {
			this._email = value;
		} else {
			console.log("Email non valida!");
		}
	}
}

Facciamo ricorso in questo caso a due proprietà: la prima, _email, intesa per contenere il valore e la seconda, email, definita tramite get e set per gestire l'accesso al valore della proprietà. Nel caso specifico, l'opzione set dell'accessor descriptor verifica che il valore da assegnare sia effettivamente un'indirizzo di email formalmente valido.

Possiamo utilizzare il metodo Object.create() per creare il costruttore di un oggetto derivato. Ad esempio, se vogliamo creare il costruttore per un oggetto programmatore derivato da persona possiamo procedere come mostrato di seguito:

function programmatore(nome, cognome)
{
	persona.call(this, nome, cognome);
	this.linguaggiConosciuti = [];
}
programmatore.prototype = Object.create(persona.prototype);

Come possiamo vedere, abbiamo invocato la funzione che definisce il costruttore di persona tramite il metodo call() in modo tale da mappare un'istanza dell'oggetto persona sull'oggetto programmatore. In pratica, l'effetto che otteniamo è l'esecuzione del corpo della funzione persona() all'interno della funzione programmatore(), riportando di fatto su programmatore le inizializzazioni previste per persona. Aggiungiamo quindi a programmatore una nuova proprietà linguaggiConosciuti. Infine, tramite Object.create(), impostiamo il prototipo di programmatore facendolo puntare a quello di persona.

Ti consigliamo anche