2012-06-05 13 views
13

Ho letto molti articoli su "ereditarietà" in javascript. Alcuni usano new mentre altri raccomandano Object.Create. Più leggo, più mi confuso poiché sembra esistere un'infinità di varianti per risolvere l'eredità.Eredità javascript corretta

Qualcuno può essere gentile a mostrarmi il modo più accettato (o defacto standard se ce n'è uno)?

(voglio avere un oggetto di base Model cui posso estendere RestModel o LocalStorageModel.)

risposta

11

Semplice: Object.create non è supportata in tutti gli ambienti, ma può essere spessorato con new. A parte questo, i due hanno obiettivi diversi: Object.create crea semplicemente un oggetto che eredita da altri, mentre newanche richiama una funzione di costruzione. Usa ciò che è appropriato.

Nel tuo caso sembra che lo RestModel.prototype erediti da Model.prototype. Object.create (o il suo spessore) è il modo corretto, allora, perché non si vuole a) creare una nuova istanza (un'istanza di un new Model) e b) non si desidera chiamare il costruttore Modello:

RestModel.prototype = Object.create(Model.prototype); 

Se si desidera chiamare il costruttore del modello su RestModels, che non ha nulla a che fare con i prototipi. Utilizzare call() o apply() per questo:

function RestModel() { 
    Model.call(this); // apply Model's constructor on the new object 
    ... 
} 
+0

Perché in questo caso 'Object.Create' è migliore di' nuovo'? E se volessi supportare anche i costruttori? E che il costruttore viene chiamato sia in 'RestModel' che in' Model' (se creo un 'RestModel'). Normalmente sono un dev di C#, quindi non ho davvero la differenza. Voglio definire il prototipo di base e utilizzare un costruttore, ma non capisco come ottenerli entrambi? – jgauffin

+1

Penso che questa risposta lo riassuma con * "Usa ciò che è appropriato." * A volte vuoi la logica nel costruttore, a volte no. +1 –

10

JavaScript è un linguaggio OOP basata su prototipi e non classi. Questa è una delle cause di tutta questa confusione che si può vedere in giro: molti sviluppatori usano JS come se fosse una classe.

Pertanto, è possibile visualizzare molte librerie che tentano di utilizzare il paradigma di un linguaggio basato su classi in JS.

Inoltre, a JS mancano alcuni tratti che rendono l'ereditarietà, anche basata sul prototipo, dolorosa. Ad esempio, prima di non c'era un modo per creare un oggetto da un altro oggetto (come dovrebbe fare un linguaggio basato sul prototipo OOP), ma usando solo un costruttore function.

E anche per questo motivo, è possibile vedere un sacco di librerie che stanno cercando di "risolvere" il linguaggio, ognuno a suo modo.

Quindi, la tua confusione è normale. Diciamo che, il modo "ufficiale" è usare la proprietà prototype, function s come costruttore e Object.create per creare dall'istanza dell'oggetto. Tuttavia, come detto sopra, in alcuni casi il codice è prolisso o privo di funzionalità, quindi questa procedura viene spesso inserita in qualche modo, e quindi diventa una questione di gusto personale.

Perché stai parlando di modello, puoi dare un'occhiata a Backbone's Model e vedere se questo approccio si adatta.

Aggiornamento: per dare qualche esempio che spero vi aiuta a chiarire, ecco il modo in cui la scuola eredità vecchio usando new:

function Model() { 
    this.foo = "foo"; 
    this.array = []; 
} 

Model.prototype.foo = ""; 
Model.prototype.array = null; 
Model.prototype.doNothing = function() {}; 

function RestModel() { 
    this.bar = "bar"; 
} 

RestModel.prototype = new Model; 
RestModel.prototype.bar = "" 

var myModel = new RestModel(); 

Ora, qui i problemi:

  1. Il costruttore di Model viene chiamato una volta, solo quando è impostato RestModel.prototype. Pertanto, la proprietà foo per ogni istanza di RestModel sarà "foo".
  2. Non solo, ma tutteRestModel le istanze condivideranno la stessa istanza dello stesso array nella proprietà this.array. Se crei direttamente un'istanza di , hai una nuova istanza di array per ogni istanza.
  3. myModel.constructor è Model anziché RestModel. Questo perché sostituiamo prototype con una nuova istanza di Model, che contiene constructor uguale a Model.
  4. Se si desidera avere una nuova istanza di RestModel con una chiamata lenta al costruttore, non è possibile.

Penso che ci siano i punti principali, ovviamente non sono gli unici. Quindi, come risolvere questi problemi? Come ho detto, molte persone lo hanno già fatto, e creano anche librerie e framework per limitare la verbosità. Tuttavia, per avere un'idea:

function Model() { 
    this.foo = "foo"; 
    this.array = []; 
} 

Model.prototype.foo = ""; 
Model.prototype.array = null; 
Model.prototype.doNothing = function() {}; 

function RestModel() { 
    Model.call(this); 

    this.bar = "bar"; 
} 

RestModel.prototype = Object.create(Model.prototype); 
RestModel.prototype.constructor = RestModel; 
RestModel.prototype.bar = "" 

var myModel = new RestModel(); 

// for lazy constructor call 
var anotherModel = Object.create(RestModel.prototype); 

RestModel.call(anotherModel); 

Si noti che se non si desidera utilizzare o prototypefunction come costruttore, con Object.create si può fare:

var Model = { 
    foo: "", 
    array: null, 
    doNothing: function() {} 
} 

var RestModel = Object.create(Model); 

RestModel.bar = ""; 

var myModel = Object.create(RestModel); 

// Assuming you have to set some default values 
initialize(RestModel); 

Oppure:

var Model = { 
    foo: "", 
    array: null, 
    doNothing: function() {}, 

    init : function() { 
     this.foo = "foo"; 
     this.array = []; 
    }, 
} 

var RestModel = Object.create(Model); 

RestModel.bar = ""; 
RestModel.init = function() { 
    Model.init.call(this); 

    this.bar = "bar"; 
} 

var myModel = Object.create(RestModel); 

myModel.init(); 

In questo caso si imita fondamentalmente il costruttore. È anche possibile passare un descriptor a Object.create (vedere lo docs) ma diventa più dettagliato. La maggior parte delle persone usa una sorta di funzione o metodo extend (come probabilmente avete visto nell'esempio Backbone).

Spero che aiuti!

+0

Quale esempio consiglia di utilizzare? – jgauffin

+0

Tutti sono validi, ma in una forma o in un'altra finiranno per essere prolissi. È possibile eseguire l'imaging se 'RestModel' ha molte nuove proprietà o metodi, ad esempio. Questo è quello che stavo parlando quando ho detto che JS manca ancora qualcosa. Per rimuovere la verbosità, o se decidi di utilizzare una libreria di terze parti, o crei una coppia di funzioni di utilità per farlo. Eviterò il puro approccio 'new' (il primo), e appena deciso per il 2 ° o 3 °, dipende se ti senti più a tuo agio ad avere qualcosa che" assomiglia "ad una" classe "(quindi al 2 °) oppure no (3 °)). – ZER0