Ricordate, nuovi oggetti possono essere creati con un costruttore, come new F().
Se F.prototype un oggetto, loperatore new si prender cura di impostare [[Prototype]] per il nuovo oggetto.
JavaScript supporta la prototypal inheritance fin dallinizio. Fu una delle caratteristiche principali del linguaggio.
Ma allinizio non cera un accesso diretto. Lunica cosa su cui ci si poteva affidare era la propriet "prototype" del costruttore, descritta in questo capitolo. Per questo, esistono ancora molti script che ne fanno utilizzo.
Da notare che qui F.prototype , sta per una comune propriet chiamata "prototype" in F. Sembra molto simile al termine prototype, ma in questo caso intendiamo realmente riferirci ad una propriet con questo nome.
Vediamo qui un esempio:
let animal = {
eats: true
};
function Rabbit(name) {
this.name = name;
}
Rabbit.prototype = animal;
let rabbit = new Rabbit("White Rabbit"); // rabbit.__proto__ == animal
alert( rabbit.eats ); // true
Impostare Rabbit.prototype = animal fa letteralmente quanto segue: Quando un nuovo new Rabbit viene creato, assegna il suo [[Prototype]] ad animal.
Questo il risultato:
In figura, "prototype" una freccia orrizzontale, ci significa che una comune propriet, mentre [[Prototype]] verticale, quindi rabbit eredita da animal.
F.prototype viene utilizzato solamente al momento in cui si invoca new FF.prototype viene utilizzata solamente quando si invoca new F, e si occupa di assegnare [[Prototype]] del nuovo oggetto.
Se, dopo la creazione, F.prototype cambia (F.prototype = <another object>), allora verr creato un nuovo oggetto con new F che avr un altro oggetto come [[Prototype]], ma gli oggetti gi esistenti faranno riferimento a quello vecchio.
Default F.prototype, la propriet constructor
Ogni funzione possiede la propriet "prototype" anche se non gliela forniamo direttamente.
Il "prototype" di default un oggetto con ununica propriet, il constructor che punta alla funzione stessa.
Vediamo un esempio:
function Rabbit() {}
/* default prototype
Rabbit.prototype = { constructor: Rabbit };
*/
Possiamo verificarlo:
function Rabbit() {}
// di default:
// Rabbit.prototype = { constructor: Rabbit }
alert( Rabbit.prototype.constructor == Rabbit ); // true
Naturalmente, se non facciamo nulla, il constructor sar disponibile a tutti i rabbit attraverso [[Prototype]]:
function Rabbit() {}
// di default:
// Rabbit.prototype = { constructor: Rabbit }
let rabbit = new Rabbit(); // eredita da {constructor: Rabbit}
alert(rabbit.constructor == Rabbit); // true (dal prototype)
Possiamo utilizzare il constructor per creare un nuovo oggetto utilizzando lo stesso costruttore delloggetto gi esistente.
Come nellesempio:
function Rabbit(name) {
this.name = name;
alert(name);
}
let rabbit = new Rabbit("White Rabbit");
let rabbit2 = new rabbit.constructor("Black Rabbit");
Questo torna molto utile quando abbiamo un oggetto, ma non sappiamo quale costruttore stato utilizzato (ad esempio se arriva da una libreria di terze parti), e abbiamo bisogno di crearne un altro dello stesso tipo.
Ma probabilmente la cosa pi importante del "constructor" che
JavaScript stesso non garantisce il giusto valore del "constructor".
E vero, esiste di default nel "prototype" delle funzioni, ma questo tutto. Ci che accade dopo solo nostra responsabilit.
In particolare, se rimpiazziamo completamente il prototype di default, allora non ci sar alcun "constructor".
Ad esempio:
function Rabbit() {}
Rabbit.prototype = {
jumps: true
};
let rabbit = new Rabbit();
alert(rabbit.constructor === Rabbit); // false
Quindi, per mantenere il "constructor" corretto, possiamo decidere di aggiungere/rimuovere propriet al "prototype" di default, invece che sovrascriverlo completamente:
function Rabbit() {}
// Non sovrascriviamo Rabbit.prototype completamente
// aggiungiamo semplicemente una propriet
Rabbit.prototype.jumps = true
// il Rabbit.prototype.constructor viene cosi preservato
O, in alternativa, possiamo ricreare il constructor manualmente:
Rabbit.prototype = {
jumps: true,
constructor: Rabbit
};
// ora il costruttore corretto, perch lo abbiamo aggiunto noi
Riepilogo
In questo capitolo abbiamo descritto brevemente il modo in cui impostare il [[Prototype]] per gli oggetti generati tramite il costruttore. Pi avanti vedremo dei pattern pi avanzati su cui fare affidamento.
Il tutto abbastanza semplice, solo alcune note per renderlo pi chiaro:
- La propriet
F.prototype(da non confondere con[[Prototype]]) imposta[[Prototype]]dei nuovi oggetti quando viene invocatonew F(). - Il valore di
F.prototypepu essere sia un oggetto chenull: altri valori verranno ignorati. - La propriet
"prototype"ha un effetto speciale quando impostata in un costruttore, ed invocata connew.
Negli oggetti comuni la propriet prototype non ha alcun significato speciale:
let user = {
name: "John",
prototype: "Bla-bla" // nessuna magia
};
Di default tutte le funzioni hanno F.prototype = { constructor: F }, quindi possiamo ottenere il costruttore di un oggetto accedendo alla sua propriet "constructor".
Commenti
<code>, per molte righe includile nel tag<pre>, per pi di 10 righe utilizza una sandbox (plnkr, jsbin, codepen)