2011-10-21 19 views
5

Quando faccio funzionare questo codice:eredità JavaScript magia

function foo() { 
    console.log("foo"); 
    var self = this; 
    this.a = function() { 
     return arguments.length == 0 ? self.b : (self.b = arguments[0]); 
    }; 
}; 
function bar() { 
}; 
bar.prototype = new foo(); 
var a = new bar(); 
var b = new bar(); 
console.log(a.a()); 
b.a(true); 
console.log(a.a()); 

ottengo il seguente output: foo, undefined, true

Quello che non capisco è come l'oggetto restituito dalla funzione di costruzione foo qualche modo riesce a destinare una nuova posizione di memoria per b. Mi aspettavo totalmente che l'ultima uscita fosse true perché ricevo solo l'output 1 foo.

Questo funziona è fantastico, ma sembra troppo magico.

Il mio problema qui è che mi piacerebbe impostare un ordine di inizializzazione (o catena). Quello che ho capito è che foo non può accettare alcun argomento, perché è chiamato solo una volta, per fornire una base (o un modello di ordinamento).

mi piacerebbe mettere a punto un molto semplice schema in cui foo ha dei parametri che deve essere passato bar-foo (perché bar eredita foo). Sto bene con qualcosa come chiamare foo.apply(this, arguments) da bar ma la chiamata per impostare il prototipo deve essere fatta senza argomenti, quello che mi serve è un modo fluente per distinguere i due a parte.

Ma io davvero non voglio costruire un intero servizio intorno creazione di oggetti che consentono una sorta di eredità, l'unica cosa che mi interessa è costruire gli oggetti, in modo corretto ...

+1

Mi viene registrato 'foo, undefined, true'. L'ultimo 'indefinito 'viene visualizzato in Strumenti per sviluppatori di Chrome, ma questo è solo il valore di ritorno di' console.log'. Hai solo 3 log comunque. – pimvdb

+0

Sì, è vero, ho appena copiato dall'output della console di Chrome, ho perso ... –

+0

Qual è esattamente la domanda? – user123444555621

risposta

3

Su una nota a margine, a volte si utilizza self e talvolta utilizzando this ... personalmente, sarei più coerente. Mi piace usare that perché this can be confusing e self is part of the BOM, ma è solo una questione di gusti.

Prova parasitic inheritance:

function foo(arg) { 
    var that = this; 
    that.a = function() { 
     return arguments.length == 0 ? that.b : (that.b = arguments[0]); 
    }; 
} 

function bar(arg) { 
    return new foo(arg); 
} 

bar.prototype = new foo(); 
var a = new bar(); // the `new` is now optional. Personally I'd remove it... 
var b = bar(); // ... like I did here! 
console.log(a.a()); 
b.a(true); 
console.log(a.a()); 
console.log(b.a()); 

... ecco l'eredità si comporta in modo più classico, invocando esplicitamente un costruttore genitore quando il costruttore viene chiamato.

+2

Odio "quello", connota qualcosa di diverso da questo. Preferisco "istanza" –

+0

@George Jempty - Giusto abbastanza. Penso che la parte importante sia essere coerenti; non usare 'this' e quindi' self' per esempio. –

+0

Sono d'accordo sulla coerenza. E so che Crockford usa "quello", quindi sono un po 'fuori di testa. –

0

Bene, questo è uno dei punti deboli (e tuttavia, punti forti) di JS. Stai incontrando un problema di modello qui. Ti suggerisco di dare un'occhiata a http://ejohn.org/blog/simple-javascript-inheritance/ - c'è una "classe" gratuita che puoi usare per aiutare a raddrizzare ciò che ci si aspetterebbe da un'eredità più "classica".

Proprio come un esempio veloce, la maggior parte dei pattern che ho visto hanno un 'oggetto' di base (che è più o meno una classe personalizzata) e il costruttore (che è più o meno 'come l'oggetto viene costruito') chiama qualcosa come "this.init.apply (this, argomenti)". Questo chiama un metodo 'init' sull'oggetto con tutti gli argomenti che sono stati passati. Quindi l'init può essere impostato, esteso, re-set, ecc. E il costruttore chiama sempre quello che è 'locale' a se stesso con gli argomenti che sono stati passati. Piuttosto pulito.

La speranza che aiuta o almeno non confonde.

0

L'ultima uscita ètrue. Sei stato ingannato dalla tua console JavaScript. L'ultimo undefined è il valore restituito dalla tua espressione.Fai i tuoi console.log() chiamate più descrittivo a brillare una luce su quello che sta succedendo:

function foo() { 
    console.log("foo constructor called"); 
    var self = this; 
    this.a = function() { 
     return arguments.length == 0 ? self.b : (self.b = arguments[0]); 
    }; 
} 
function bar() { 
} 
bar.prototype = new foo(); 
var a = new bar(); 
var b = new bar(); 
console.log("value from a.a(): " + a.a()); 
b.a(true); 
console.log("value from a.a(): " + a.a()); 
console.log("done logging"); 

Questo produce il seguente output nella console:

foo constructor called 
value from a.a(): undefined 
value from a.a(): true 
done logging 
undefined 
0

Non è magia, è l'ereditarietà prototipale. Penso che quello che stai cercando di ottenere sia combinare due funzioni di costruzione per essere chiamato automaticamente. Probabilmente stai confondendo costruttori e prototipi.

Ma io davvero non voglio costruire un intero servizio intorno creando oggetti che consentono una sorta di eredità, l'unica cosa che veramente interessa è costruire gli oggetti, in modo corretto ...

Non so cosa intendi per "servizio". Ma come in qualsiasi altro linguaggio di programmazione orientato agli oggetti, le chiamate al metodo della superclasse devono essere eseguite manualmente. Quindi, o si codificare la "superclasse" come questo:

function foo() { 
    console.log("foo"); 
    var self = this; 
    this.a = function() { 
     return arguments.length == 0 ? self.b : (self.b = arguments[0]); 
    }; 
} 
function bar() { 
    foo.apply(this, arguments); // <- ugly 
} 
//bar.prototype = new foo(); // this is not neccessary 

... o si costruisce alcune cose wrapper per collegare le "classi". Qualcosa di simile:

function inherit(superconstructor) { 
    return function() { 
     superconstructor.apply(this, arguments); 
    }; 
} 
function foo() {...} 
bar = inherit(foo);