Dichiarazione del metodo dell’object javascript nella funzione di costruzione e nel prototipo

Nella creazione di oggetti javascript, posso inserire una dichiarazione di metodo nella funzione di costruzione o nel prototipo. Ad esempio, dire che voglio una class Dog che abbia una proprietà Name e un metodo Bark. Posso inserire la dichiarazione del metodo Bark nella funzione di costruzione:

var Dog = function(name) { this.Name = name; this.Bark = function() { alert(this.Name + " bark"); }; } 

o potrei inserire come metodo sull’object prototipo:

 var Dog = function(name) { this.Name = name; } Dog.prototype.Bark = function() { alert(this.Name + " bark"); }; 

Quando istanzio oggetti di tipo Dog, entrambi gli approcci sembrano funzionare correttamente:

 var dog = new Dog("Fido"); dog.Bark(); //Both approaches show "Fido bark" 

Dovrei preferire uno di questi approcci all’altro? Ci sono dei vantaggi nell’usare l’uno sull’altro? Dietro le quinte, questi due approcci finiscono per fare esattamente la stessa cosa? Quale approccio la maggior parte delle persone tende a favorire?

Grazie per l’aiuto.

Per l’esempio che dai, dovresti usare l’approccio prototipo. In generale, dipende. Il vantaggio principale del primo approccio (inizializzare i metodi nel costruttore) è che è ansible sfruttare le chiusure sfruttando le variabili locali definite all’interno del costruttore nei propri metodi. Queste variabili non sono direttamente accessibili al di fuori della funzione di costruzione, quindi sono effettivamente “private”, il che significa che la tua API è più pulita rispetto a se queste variabili fossero definite come proprietà dell’object. Alcune regole pratiche generali:

  • Se i tuoi metodi non usano variabili locali definite nel tuo costruttore (il tuo esempio non lo fa), allora usa l’approccio prototipo.
  • Se stai creando molti Dog , usa l’approccio prototipo. In questo modo, tutte le “istanze” (cioè gli oggetti creati dal costruttore Dog ) condivideranno un insieme di funzioni, mentre il modo costruttore, un nuovo insieme di funzioni viene creato ogni volta che viene chiamato il costruttore Dog , utilizzando più memoria.
  • Se stai creando un piccolo numero di Dog s e scopri che l’utilizzo di variabili locali “private” nel tuo costruttore migliora il tuo codice, questo potrebbe essere l’approccio migliore. Usa il tuo giudizio e fai alcuni benchmark se le prestazioni o il consumo di memoria sono problemi importanti.

È ansible utilizzare un approccio ibrido in base al quale solo i metodi che richiedono l’accesso alle variabili del costruttore privato locale vengono definiti nel costruttore, mentre altri metodi vengono assegnati al prototipo.

Ad esempio, il codice seguente utilizza una variabile locale nel costruttore per tenere traccia del numero di volte in cui questo cane ha abbaiato mantenendo il numero reale privato, quindi i metodi correlati all’abbaio sono definiti all’interno del costruttore. La scodinzolare della coda non richiede l’accesso al numero di cortecce, pertanto tale metodo può essere definito sul prototipo.

 var Dog = function(name) { this.name = name; var barkCount = 0; this.bark = function() { barkCount++; alert(this.name + " bark"); }; this.getBarkCount = function() { alert(this.name + " has barked " + barkCount + " times"); }; }; Dog.prototype.wagTail = function() { alert(this.name + " wagging tail"); }; var dog = new Dog("Dave"); dog.bark(); dog.bark(); dog.getBarkCount(); dog.wagTail(); 

I due sono diversi: il primo memorizzerà il riferimento al metodo solo sull’object prototipo, mentre la seconda soluzione memorizzerà il metodo su ciascun object. Ciò significa che ogni object conterrà un puntatore aggiuntivo e quindi occuperà un po ‘più di memoria ciascuno.

Il metodo per object consente al metodo di riferirsi alle variabili nel costruttore (una chiusura) e quindi consente di accedere ad alcuni dati che non è ansible accedere da un metodo prototipo.

Infine, un metodo prototipo può essere modificato in seguito , ovvero è ansible ridefinire Bark in fase di runtime sull’object prototipo, e questa modifica funzionerà per tutti gli oggetti con questo prototipo (poiché il metodo viene sempre controllato tramite il prototipo).

La stragrande maggioranza del codice javascript che ho visto utilizza il metodo prototipo. Penso che ci siano tre ragioni per cui posso pensare in cima alla mia testa.

La prima è che si evita che ogni class sia un enorme costruttore: la logica del costruttore va nella funzione di costruzione, la logica per altri metodi è dichiarata altrove – questa è principalmente una cosa di chiarezza / separazione delle cose di preoccupazione, ma in javascript è necessario ogni bit di chiarezza puoi mettere le mani su

Il secondo è l’efficienza. Quando si dichiarano i metodi nel costruttore, si crea una nuova istanza dell’object function per ogni istanza dell’object e si associa anche l’ambito del costruttore a ciascuna di queste funzioni (ovvero, possono fare riferimento, ad esempio, argomenti al costruttore, che non possono mai essere gc’d finchè l’object vive). Quando si dichiarano i metodi sul prototipo, esiste una singola copia dell’object funzione che viene utilizzata da tutte le istanze – le proprietà del prototipo non vengono copiate sulle istanze.

Una terza ragione è che è ansible “estendere” una class in vari modi quando si utilizza il metodo prototipo, come il concatenamento del prototipo utilizzato da Backbone.js e dal costrutto di class di CoffeeScript.