I costruttori in JavaScript sono differenti da quelli di molti altri linguaggi.
Qualsiasi chiamata a funzione preceduta dalla parola chiave new agisce come
un costruttore.
Dentro al costruttore (la funzione chiamata) il valore di this fa riferimento
al nuovo oggetto creato. Il prototype di questo nuovo
oggetto viene impostato al prototype dell'oggetto funzione che è stato invocato
come costruttore.
Se la funzione che è stata chiamata non ha un'istruzione return esplicita,
allora essa ritorna implicitamente il valore di this (il nuovo oggetto).
function Person(name) {
this.name = name;
}
Person.prototype.logName = function() {
console.log(this.name);
};
var sean = new Person();
Questo esempio chiama Person come costruttore ed imposta il prototype del
nuovo oggetto creato a Person.prototype.
In caso di istruzione return esplicita, la funzione ritorna il valore
specificato da quell'istruzione, ma solo se il valore di ritorno è un
Object.
function Car() {
return 'ford';
}
new Car(); // un nuovo oggetto, non 'ford'
function Person() {
this.someValue = 2;
return {
name: 'Charles'
};
}
new Person(); // l'oggetto ritornato ({name: 'Charles'}), escluso someValue
Quando la parola chiave new viene omessa, la funzione non ritornerà un
nuovo oggetto.
function Pirate() {
this.hasEyePatch = true; // imposta la proprietà nell'oggetto globale!
}
var somePirate = Pirate(); // somePirate è undefined
Mentre l'esempio precedente potrebbe sembrare essere funzionante in alcuni
casi, a causa del modo in cui lavora this in JavaScript,
esso userà l'oggetto globale come valore di this.
Per poter omettere la parola chiave new, la funzione costruttore deve
esplicitamente ritornare un valore.
function Robot() {
var color = 'gray';
return {
getColor: function() {
return color;
}
}
}
Robot.prototype = {
someFunction: function() {}
};
new Robot();
Robot();
Entrambe le chiamate a Robot ritornano lo stesso risultato, un nuovo oggetto
creato con una proprietà chiamata method, che è una Closure.
Bisogna anche notare che la chiamata new Robot() non influisce sul prototipo
dell'oggetto ritornato. Mentre il prototipo sarà impostato con il nuovo oggetto
creato, Robot non ritornerà mai quel nuovo oggetto.
Nell'esempio sopra, non c'è differenza funzionale nell'usare o meno la parola
chiave new.
Viene spesso raccomandato di non usare new perché una sua dimenticanza
può portare a bug potenzialmente insidiosi da risolvere.
Per poter creare un nuovo oggetto, si dovrebbe invece usare una factory e costruire un nuovo oggetto all'interno di quella factory.
function CarFactory() {
var car = {};
car.owner = 'nobody';
var milesPerGallon = 2;
car.setOwner = function(newOwner) {
this.owner = newOwner;
}
car.getMPG = function() {
return milesPerGallon;
}
return car;
}
Sebbene questo esempio sia a prova di omissione della parola chiave new e
renda sicuramente più semplice l'utilizzo delle variabili private,
esso ha alcuni aspetti negativi.
- Usa più memoria dal momento che gli oggetti creati non condividono i metodi di un prototipo.
- Per poter ereditare, la factory deve copiare tutti i metodi da un altro oggetto oppure mettere quell'oggetto nel prototipo del nuovo oggetto.
- Perdere la catena di prototipi solo perché si vuole tralasciare la
parola chiave
newè contrario allo spirito del linguaggio.
Sebbene l'omissione della parola chiave new possa portare all'introduzione di
bug, non è certo un motivo per privarsi completamente dell'uso dei prototipi.
Alla fine si tratta di decidere quale sia la soluzione più adatta per
l'applicazione. È specialmente importante scegliere uno specifico stile
di creazione degli oggetti ed usarlo in maniera consistente.