Come creare gli oggetti in Javascript

Nella lezione precedente abbiamo introdotto alcuni concetti fondamentali sugli oggetti. In questa lezione, invece, entriamo nel vivo vedendo come è possibile crearli.

In Javascript esistono tre modi per creare un oggetto. Vediamoli in ordine.

Oggetti letterali

Il primo modo di creare un oggetto in Javascript è quello di utilizzare i letterali, cioè espressioni del linguaggio che, quando valutate, creano un oggetto. In gergo Javascript, questi oggetti vengono chiamati Object literal.

Di base, un Object literal è una sequenza di proprietà divise da virgole e racchiuse tra parentesi graffe. Le proprietà, ossia le coppie chiave/valore, sono a loro volta specificate separando nome e valore da due punti. Per comprendere meglio, vediamo qualche esempio:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// Un oggetto vuoto senza proprietà
let vuoto = {};

// Un semplice oggetto
let punto = {
    x: 0,
    y: 0
};

// Un altro oggetto
let film = {
    nome: "Guerre Stellari",
    regista: "George Lucas",
    anno: 1977
}

Da questi esempi si può vedere come sia semplice creare oggetti letterali. Un oggetto letterale è un'espressione la cui valutazione comporta la creazione di un oggetto. Ogniqualvolta l'espressione viene valutata, un nuovo e distinto oggetto viene creato. Come si vede dall'esempio, le proprietà degli oggetti sono indicate da un nome, che è una stringa ma senza i doppi apici, e un valore separati da due punti.

La sintassi mostrata qui è quella valida sin dalle prime versioni di Javascript. Le versioni nuove del linguaggio hanno introdotto nuove caratteristiche che vedremo più avanti.

Una piccola nota sulle virgole. In Javascript è perfettamente valido lasciare una virgola dopo l'ultima proprietà nel letterale di un oggetto come in questo esempio:

1
2
3
4
5
6
// Un altro oggetto
let film = {
    nome: "Guerre Stellari",
    regista: "George Lucas",
    anno: 1977,
}

Questo è valido e, a volte, anche incoraggiato in quanto si evita di commettere errori quando, successivamente, si vogliono aggiungere altre proprietà all'oggetto in questione.

Operatore new

In Javascript è possibile usare l'operatore new per creare un oggetto. Questo operatore deve essere seguito da una funzione particolare che prende il nome di costruttore. Una funzione costruttore ha lo scopo di inizializzare l'oggetto appena creato.

Alcuni degli oggetti predefiniti di Javascript hanno dei costruttori pronti all'uso:

1
2
3
let x = new Object();
let y = new Array();
let z = new Map();

Inoltre, è possibile definire dei costruttori personalizzati per i propri oggetti in maniera tale da specificare il modo in cui essi vengono inizializzati. Ma per far questo dobbiamo prima studiare le classi che vedremo più avanti.

Prototipi e Object.create()

L'ultimo modo con cui è possibile creare oggetti in Javascript consiste nell'utilizzo dei prototipi.

Quasi tutti gli oggetti in Javascript hanno un secondo oggetto associato: il prototipo. Gli oggetti, oltre ad avere le loro proprietà, own properties, ereditano anche le proprietà del prototipo.

Ad esempio, se un oggetto A ha le proprietà x e y, se creiamo un oggetto B che ha come prototipo A, allora B avrà anch'esso le proprietà x e y ereditate da A. Per poter far questo bisogna usare la funzione Object.create. La funzione Object.create necessita come parametro di un oggetto che funge da prototipo. Osserviamo questo esempio:

1
2
3
4
5
6
let A = {
    x: 1,
    y: 2
};

let B = Object.create(A);

In questo esempio abbiamo creato l'oggetto B usando come prototipo l'oggetto A. A questo punto B avrà tutte le proprietà di A: x e y. Infatti, se eseguiamo il seguente codice:

1
console.log(B.y); // Output: 2

Avremo come output 2, questo perché la proprietà y è stata ereditata da A.

Bisogna notare una cosa importante però. Le proprietà ereditate vanno considerate alla stregua di copie. Infatti, modificando la proprietà x o y non significa che le rispettive proprietà di A vengono modificate. Infatti, guardano al prossimo esempio:

1
2
3
4
5
6
7
B.x = 3;
B.y = 4;

console.log(A.x); // Output: 1
console.log(A.y); // Output: 2
console.log(B.x); // Output: 3
console.log(B.y); // Output: 4

Le proprietà di A sono rimaste immutate. Il vantaggio di usare i prototipi è che così siamo in grado di creare oggetti simili in funzionalità a quelli usati come prototipo utilizzando una sola riga di codice.

In Javascript, quasi tutti gli oggetti hanno un prototipo, anche se questo non viene esplicitamente indicato. Infatti, gli oggetti letterali che abbiamo creato sopra hanno tutti lo stesso prototipo ereditato da Object.prototype. Object è una classe che definisce il comportamento base di quasi tutti gli oggetti in Javascript. Vedremo le classi più avanti, per il momento basta sapere che Object definisce alcune proprietà fondamentali e che, attraverso il prototipo, gli oggetti le ereditano per poterle usare.

Per gli oggetti creati con new la questione è leggermente più complessa. Infatti essi hanno come prototipo quello della funzione costruttore che, essendo anch'essa un oggetto in Javascript, ha il suo prototipo. Questa cosa può confondere all'inizio, però ricordando che ogni cosa che non sia una stringa, numero o booleano in Javascript è un oggetto, possiamo facilmente capire come una funzione sia anch'essa un oggetto. Tuttavia, se il costruttore non definisce un prototipo esso eredita quello di Object per cui, di solito, anche gli oggetti creati con new hanno come prototipo Object.prototype. I costruttori e gli oggetti creati con new li vedremo, però, nel dettaglio più avanti.

In altri casi, come per esempio per l'oggetto Array che vedremo nelle prossime lezioni, il costruttore definisce un prototipo diverso, per cui un oggetto creato con new Array() ha come prototipo Array.prototype.

Il procedimento può essere propagato. Ad esempio, possiamo creare un oggetto a partire da un altro sfruttandolo come prototipo e così via, creando una catena di prototipi (prototype chain). In questo modo l'ultimo oggetto della catena ottiene tutte le proprietà degli oggetti precedenti.

Per comprendere come funziona la catena di prototipi guardiamo l'esempio, simile al precedente:

1
2
3
4
5
6
let punto1 = {
    x: 1,
    y: 2
};

let punto2 = Object.create(punto1);

In questo esempio, l'oggetto punto2 avrà come prototipo l'oggetto punto1 per cui ne erediterà le proprietà x e y. Inizialmente punto2 avrà come valori delle proprietà x e y gli stessi di punto1. Però, se li modifichiamo, i nuovi valori non si propagheranno a punto1. Quindi, abbiamo effettivamente creato un oggetto diverso punto2 con le stesse proprietà di punto1 ma valori diversi.

Nel nostro esempio, punto1 ha un prototipo, che è Object.prototype ma non ha nessuna proprietà prototype. Per questo motivo punto2 eredita le proprietà di punto1, in quanto suo prototipo diretto, e le proprietà di Object che è prototipo di punto1. Abbiamo, a tutti gli effetti, creato una catena di prototipi: Object -> punto1 -> punto2. Inoltre, Object definisce il comportamento di punto1 e punto2.

In Javascript, quasi tutti gli oggetti hanno un prototipo, ma solo pochi oggetti hanno una proprietà prototype. Questi ultimi oggetti sono quelli che definiscono le proprietà base di tutti gli altri oggetti.

Con Object.create è anche possibile ottenere lo stesso effetto di un Oggetto letterale:

1
2
let x = Object.create({});
let y = Object.create(Object.prototype);

le due righe sono equivalenti tra loro e sono anche equivalenti, per quanto detto prima, all'espressione let x = {};.

Con Object.create è anche possibile creare oggetti che non hanno prototipo:

let x = Object.crate(null);

Questo oggetto non erediterà nessuna proprietà da Object. L'utilità di un oggetto privo di prototipo emerge in casi particolari che vedremo più avanti.

Riassumendo, Object.create è una tecnica di creazione degli oggetti che fornisce l'abilità di creare oggetti con prototipo arbitrario. Questa abilità è molto potente e la vedremo applicata più avanti.

Riassumendo

In questa lezione abbiamo visto i tre modi con cui, in Javascript, è possibile creare oggetti:

  • Object literals
  • Operatore new
  • Object.create

L'ultima tecnica è la più avanzata e permette di creare oggetti con prototipo arbitrario. Più avanti ne vedremo i dettagli e le applicazioni.

Nella prossima lezione, invece, vedremo come accedere in lettura e scrittura alle proprietà degli oggetti.