2009-01-07 15 views
18

Puoi spiegare la differenza tra i metodi di impostazione nel costruttore e attraverso l'oggetto prototipo? Il codice seguente mostra questi due modi di impostare i metodi - say_hello e say_bye entrambi funzionano bene:Impostazione dei metodi tramite oggetto prototipo o in costruttore, differenza?

function MessageClass() { 
    this.say_bye = function() { alert('see ya'); }; 
} 

MessageClass.prototype.say_hello = function() { alert('hello'); }; 

x = new MessageClass(); 
x.say_hello(); 
x.say_bye(); 

risposta

31

foxxtrot e annakata sono entrambi corretti, ma mi butto i miei 2 centesimi.

Se si utilizza il prototipo, ciascuna istanza di "MessageClass" fa realmente riferimento alle stesse funzioni. Le funzioni esistono in memoria solo una volta e sono utilizzate per tutte le istanze. Se si dichiarano i metodi nel costruttore (o in altro modo lo si aggiunge a un'istanza specifica) anziché il prototipo, viene creata una nuova funzione per ogni istanza di MessageClass.

Detto questo, non c'è probabilmente alcuna differenza di prestazioni notevole nella maggior parte dei casi ed è improbabile che si notino anche differenze di utilizzo della memoria. Vorrei andare con il metodo prototipo a meno che non si abbia una ragione convincente per fare diversamente. L'unica ragione per cui posso dichiarare un metodo nel costruttore è se hai bisogno di una chiusura. Ad esempio, se si dispone di gestori di eventi o si voleva simulare le proprietà private con getter/setter si potrebbe fare:

function MessageClass() { 
    var self = this; 
    this.clickHander = function(e) { self.someoneClickedMe = true; }; 

    var _private = 0; 
    this.getPrivate = function() { return _private; }; 
    this.setPrivate = function(val) { _private = val; }; 
} 

EDIT: perché non c'è stata discussione su come questi effetti oggetti estesi da un altro oggetto con funzioni assegnato nel costruttore sto aggiungendo un po 'più di dettaglio. Potrei usare il termine "classe" per semplificare la discussione, ma è importante notare che js non supporta le classi (ciò non significa che non possiamo fare un buon sviluppo OO) o non discuteremo di questo problema.

La maggior parte delle librerie javascript chiama il costruttore sulla classe base e sulla sottoclasse. (ad esempio, Object.extend di Prototype.js) Ciò significa che i metodi assegnati nel costruttore di ciascuno saranno disponibili sugli oggetti risultanti. Tuttavia, se stai estendendo gli oggetti da solo, possono esserci conseguenze inaspettate.

Se prendo le MessageClass sopra ed estenderlo:

function ErrorMessageClass() {} 
ErrorMessageClass.prototype = new MessageClass(); 

errorMsg = new ErrorMessageClass(); 

Poi errorMsg avrà un getPrivate e il metodo setPrivate su di esso, ma non possono comportarsi come ci si aspetterebbe. Poiché tali funzioni erano a livello di ambito quando venivano assegnate (ad esempio in "ErrorMessageClass.prototype = new MessageClass()" non solo i metodi get/setPrivate sono condivisi, anche la variabile _private viene condivisa tra tutte le istanze di ErrorMessageClass. proprietà statica per ErrorMessageClass.Ad esempio:

var errorA = new ErrorMessageClass(); 
var errorB = new ErrorMessageClass(); 
errorA.setPrivate('A'); 
console.log(errorA.getPrivate()); // prints 'A' 
console.log(errorB.getPrivate()); // prints 'A' 
errorB.setPrivate('B'); 
console.log(errorA.getPrivate()); // prints 'B' 

Allo stesso modo con la funzione clickHandler e someoneClickedMe proprietà:

errorA.clickHandler(); 
console.log(errorA.someoneClickedMe); // prints 'true' 
console.log(errorB.someoneClickedMe); // prints 'true' 

Tuttavia, modificare tali definizioni di funzioni da utilizzare this._private:

this.getPrivate = function() { return this._private; }; 
this.setPrivate = function(val) { this._private = val; }; 

e comportamento di istanze ErrorMessageClass diventa più di quello che ti aspetteresti:

errorA.setPrivate('A'); 
errorB.setPrivate('B'); 
console.log(errorA.getPrivate()); // prints 'A' 
console.log(errorB.getPrivate()); // prints 'B' 
+0

accidenti a te per la tua eloquenza :) (anche se non sono d'accordo non c'è una differenza evidente) – annakata

+0

Anche se si usa this._private, la variabile è accessibile dall'esterno. – Chris

+0

(tecnicamente parlando, è possibile rintracciare un debugger in un modo o nell'altro, se non tutti, ambienti JS) – Chris

5

La differenza è quando si deriva una classe da Classe messaggio. Solo i metodi dichiarati sul prototipo saranno disponibili sulle classi figlio di Messaggio.

+0

Quindi, se avessi "BetterMessageClass.prototype = new MessageClass", solo "say_hello" sarebbe stato ereditato? – snitko

+0

Sono in grado di chiamare il metodo "say_bye" (che è dichiarato come "this") dalla classe figlio. Ecco il mio codice: function Employee() { this.say_bye = function() {alert ('see ya'); }; } function Manager() { } Manager.prototype = new Employee; Manager.prototype.constructor = Manager; var manager = new Manager(); manager.say_bye(); – Raghav

+1

@foxxtrot Questo non è corretto. Anche i metodi dichiarati su 'this' all'interno del costruttore sono accessibili in una classe figlio. Si consideri il codice 'errorA.setPrivate ('A')' di Prestaul, che sta accedendo a un metodo dichiarato su 'this' dalla classe padre' MessageClass'. – Suneel

6

Se si associano i metodi con il prototipo JS, è necessario farlo una sola volta e associare a una classe oggetto (che lo rende idonea per le estensioni OO JS).

Se si esegue il bind all'interno della funzione "classe", JS deve eseguire il lavoro di creazione e assegnazione per ciascuna istanza.

+0

Si tratta di un problema di prestazioni? –

+0

sì, sicuramente – annakata

+0

Cosa succede se prima definisco i metodi come funzioni, quindi dico quanto segue nel costruttore: 'this.say_bye = MessageClass_sayhello; – snitko