La flessibilità degli oggetti JavaScript si esprime principalmente nella possibilità di modificare la struttura anche dopo la creazione. Anche utilizzando un costruttore per creare un oggetto, continuiamo a disporre di questa possibilità.
Riprendendo il costruttore creato nelle lezioni precedenti (persona
), possiamo ad esempio scrivere il seguente codice:
var marioRossi = new persona("Mario", "Rossi");
var giuseppeVerdi = new persona("Giuseppe", "Verdi");
marioRossi.telefono = "0612345678";
creando una nuova proprietà telefono
per l'oggetto marioRossi
, senza influenzare la struttura di giuseppeVerdi
. In sostanza, nella creazione di oggetti possiamo partire da una struttura comune definita da un costruttore per poi personalizzarla in base alle nostre esigenze.
Ma come fare per modificare la struttura di tutti gli oggetti creati tramite un costruttore? Se ad esempio, dopo aver creato diversi oggetti dal costruttore persona()
vogliamo aggiungere per tutti la proprietà telefono
, possiamo sfruttare una delle carattersitiche più interessanti della programmazione ad oggetti di JavaScript: il prototype.
Nel nostro caso procederemo nel seguente modo:
persona.prototype.telefono = "123456";
Questo assegnamento fa sì che tutti gli oggetti creati tramite il costruttore persona()
abbiano istantaneamente tra le loro proprietà anche la proprietà telefono
valorizzata al valore indicato.
Ad essere precisi, la nuova proprietà non è direttamente agganciata a ciascun oggetto, ma accessibile come se fosse una sua proprietà. Questo grazie al meccanismo di prototyping che sta alla base dell'ereditarietà nella programmazione ad oggetti in JavaScript.
In JavaScript, il prototipo di un oggetto è una sorta di riferimento ad un altro oggetto. Gli oggetti che creiamo tramite la semplice applicazione della notazione letterale hanno come prototipo Object
.
Quando creiamo un oggetto tramite un costruttore, il suo prototipo è l'oggetto prototype
del costruttore.
Ereditarietà prototipale
Il meccanismo su cui si basa l'erediterietà prototipale (prototypal inheritance) di JavaScript è abbastanza semplice: se una proprietà non si trova in un oggetto viene cercata nel suo prototipo.
Il prototipo di un oggetto può a sua volta avere un altro prototipo. In questo caso la ricerca di una proprietà o di un metodo risale la catena dei prototipi fino ad arrivare all'oggetto Object
, il prototipo base di tutti gli oggetti.
Anche gli oggetti predefiniti di JavaScript hanno un prototipo di riferimento. La loro gestione, nella maggior parte dei casi, è del tutto analoga alla gestione dei prototipi degli oggetti creati tramite un nostro costruttore. Questo ci consente di estendere funzionalità non previste dagli oggetti predefiniti in modo abbastanza semplice ed elegante.
Ad esempio, se vogliamo rendere disponibile per tutte le stringhe un metodo per effettuare il padding
, possiamo intervenire sul prototype
del costruttore String()
, come mostrato di seguito:
String.prototype.padLeft = function(width, char) {
var result = this;
char = char || " ";
if (this.length < width) {
result = new Array(width - this.length + 1).join(char) + this;
}
return result;
};
Grazie a questa definizione possiamo utilizzare il metodo padLeft()
come se fosse un metodo predefinito, come mostrato nel seguente esempio:
console.log("abc".padLeft(10, "x")); //"xxxxxxxabc"