Gli oggetti non ereditano funzioni prototipate

Ho una funzione di costruzione, che funge da superclass:

Bla = function(a){this.a = a;} 

Lo prototipo per includere un metodo semplice:

 Bla.prototype.f = function(){console.log("f"); 

E ora il nuovo Bla(1).f(); registrerà “f” nella console. Ma, diciamo che ho bisogno di una sottoclass che erediti da Bla:

 Bla2 = function(a) { this.base = Bla; this.base(); } x = new Bla2(5); 

Ora, come previsto, xa mi dà 5 . Ma xf undefined è undefined ! Sembra che Bla2 non l’abbia ereditato dalla class Bla ! Perché sta succedendo questo e come lo correggo?

Sembra che Bla2 non l’abbia ereditato dalla class Bla !

Destra. Non hai fatto nulla per colbind l’ereditarietà lì, hai appena creato un membro di Bla2 chiamato base che è un’istanza di Bla . base non è un identificatore speciale in JavaScript.

Il modo tipico per impostare l’ereditarietà in JavaScript è il seguente:

 // The base constructor function function Base(x) { // Base per-instance init this.x = x; } // An example Base function Base.prototype.foo = function() { console.log("I'm Base#foo, x = " + this.x); }; // The Derived constructor function function Derived(x, y) { // Normally you need to call `Base` as it may have per-instance // initialization it needs to do. You need to do it such that // within the call, `this` refers to the current `this`, so you // use `Function#call` or `Function#apply` as appropriate. Base.call(this, x); // Derived per-instance init this.y = y; } // Make the Derived.prototype be a new object backed up by the // Base.prototype. Derived.prototype = Object.create(Base.prototype); // Fix up the 'constructor' property Derived.prototype.constructor = Derived; // Add any Derived functions Derived.prototype.bar = function() { console.log("I'm Derived#bar, x = " + this.x + ", y = " + this.y); }; 

… dove Object.create da ES5, ma è una delle cose che può essere facilmente ingrandita. (Oppure puoi usare una funzione che fa solo il minimo Object.create senza provare a fare tutto di Object.create , vedi sotto). E poi lo usi:

 var d = new Derived(4, 2); d.foo(); // "I'm Base#foo, x = 4" d.bar(); // "I'm Derived#bar, x = 4, y = 2" 

Esempio dal vivo | fonte

Nel codice precedente a volte viene visualizzato il Derived.prototype in questo modo:

 Derived.prototype = new Base(); 

… ma c’è un problema nel farlo in questo modo: Base può eseguire l’inizializzazione per istanza che non è appropriata per l’ereditarietà di Derived . Potrebbe anche richiedere argomenti (come fa la nostra Base , cosa passeremmo per x ?). Invece facendo diventare Derived.prototype solo un nuovo object supportato da Base.prototype , otteniamo le cose giuste. Quindi chiamiamo Base da Derived per ottenere init per-instance.

Quanto sopra è molto semplice e come puoi vedere comporta una serie di passaggi. Fa anche poco o nulla per rendere i “superclass” facili e altamente mantenibili. Ecco perché vedi tanti script di “eredità” là fuori, come Prototype’s Class , Dean Edwards ‘Base2, o (tossire) il mio Lineage .


Se non puoi fare affidamento sull’avere funzionalità ES5 nel tuo ambiente e non vuoi includere uno shim che faccia le basi di Object.create , puoi semplicemente usare questa funzione al suo posto:

 function simpleCreate(proto) { function Ctor() { } ctor.prototype = proto; return new Ctor(); } 

Poi invece di

 Derived.prototype = Object.create(Base.prototype); 

faresti:

 Derived.prototype = simpleCreate(Base.prototype); 

Ovviamente, puoi fare di più per automatizzare le operazioni di hook-up, il che è tutto in Lineage .


… e infine: Sopra ho usato le funzioni anonime per semplicità, ad es .:

 Base.prototype.foo = function() { // ... }; 

… ma non lo faccio nel mio vero codice, perché mi piace aiutare i miei strumenti ad aiutarmi . Quindi tendo ad usare il modello del modulo attorno a ciascuna “class” (funzione costruttore e prototipo associato) e uso le dichiarazioni di funzione (dato che lavoro per il web, e IE7 e IE8 hanno ancora problemi con le espressioni di funzioni con nome. t usando Lineage , farei quanto sopra in questo modo:

 // Base (function(target) { // Base constructor target.Base = Base; function Base(x) { // Base per-instance init this.x = x; } // An example Base function Base.prototype.foo = Base$foo; function Base$foo() { console.log("I'm Base#foo, x = " + this.x); } })(window); // Derived (function(target, base) { // The Derived constructor function target.Derived = Derived; function Derived(x, y) { // Init base base.call(this, x); // Derived per-instance init this.y = y; } // Make the Derived.prototype be a new object backed up by the // Base.prototype. Derived.prototype = Object.create(base.prototype); // Fix up the 'constructor' property Derived.prototype.constructor = Derived; // Add any Derived functions Derived.prototype.bar = Derived$bar; function Derived$bar() { console.log("I'm Derived#bar, x = " + this.x + ", y = " + this.y); } })(window, Base); 

…o qualcosa di simile. Copia live | fonte