2013-03-19 10 views
5

Quando un prototipo è impostato su una funzione di costruzione, l'operatore instanceof restituisce solo true fino a quando il prototipo non viene modificato. Perché?restituisce false sulla successiva modifica alla catena di ereditarietà

function SomeConstructorFunction() { 
} 

function extendAndInstantiate(constructorFn) { 
    constructorFn.prototype = {}; //Can be any prototype 
    return new constructorFn(); 
} 

var child1 = extendAndInstantiate(SomeConstructorFunction); 
console.log(child1 instanceof SomeConstructorFunction); //true 

var child2 = extendAndInstantiate(SomeConstructorFunction); 
console.log(child1 instanceof SomeConstructorFunction); //false 
console.log(child2 instanceof SomeConstructorFunction); //true 

risposta

3

L'ereditarietà del prototipo può essere un po 'un rompicapo del cervello. Ho già scritto una semplice spiegazione dell'ereditarietà prototipale nella seguente risposta e suggerisco di leggerla: https://stackoverflow.com/a/8096017/783743

Ora per rispondere alla tua domanda.Si consideri la seguente funzione:

function F() {} 

posso creare un'istanza di F usando new come segue:

var f = new F; 

Come previsto f instanceof F rendimenti true. Questo perché l'operatore instanceof controlla F.prototype nella catena del prototipo di f e restituisce true se è presente. Vedere la risposta che ho collegato sopra.

Ora dicono creo una nuova funzione G come segue e impostare F.prototype-G.prototype:

function G() {} 

F.prototype = G.prototype; 

Se io ora valuto f instanceof F nuovo false viene restituito. Questo perché F.prototype non è più nella catena di prototipi di f (ricorda che F.prototype è ora G.prototype).

Ora creiamo una nuova istanza di F:

var g = new F; 

se si valutano g instanceof G si tornerà true anche se g = new F. Questo perché G.prototype esiste nella catena del prototipo di g.

Questo non è un bug. È il modo in cui JavaScript funziona. In effetti possiamo sfruttare questa funzionalità per creare alcune funzioni davvero interessanti: Instantiate JavaScript functions with custom prototypes

+0

Grande spiegazione dell'ereditarietà prototipale. Penso che la confusione dell'OP risieda in 'constructorFn.prototype = {}; // Può essere qualsiasi prototipo'. Si aspetta che l'impostazione del prototipo sullo stesso ** valore ** comporterà la presenza di 'instanceof', quando invece dovrebbero essere impostati sullo stesso ** riferimento **. – MattDiamant

+0

@MattDiamant - Sono d'accordo. Non so cosa l'OP sta cercando di ottenere usando 'extendAndInstantiate' ma credo che sia un po 'come la mia funzione 'instantiate' nella seguente domanda: http://stackoverflow.com/q/11490606/783743 –

+0

Buona spiegazione . Ancora più importante, mi mancava il fatto che l'impostazione del prototipo in '{}' cambia in modo efficace il tipo di 'SomeConstructorFunction' in' Object'. 'SomeConstructorFunction.constructor' è' function Object ... '. – Stephen

3
constructorFn.prototype = {}; //Can be any prototype 

Non è vero!

constructorFn.prototype = Object.prototype; 

O qualsiasi altro prototipo nativo li renderà tutti veri.

Il motivo è che la prima volta che si chiama extendAndInstantiate(), si imposta il prototipo della SomeConstructorFunction su qualcosa (qui un oggetto vuoto {}). Per child1, instanceOf restituirà true mentre il prototipo di SomeConstructorFunction è l'istanza esatta di {}. Quando si esegue extendAnInstantiate() una seconda volta, stai cambiando SomeConstructorFunction 's prototipo a una diversa istanza di {}, così child2 sarà un instanceof quel nuovo {}, ma child1 è ancora un instanceof del vecchio {}, così child1 tornerà falso, mentre child2 restituisce true. Se si imposta il prototipo su un prototipo comune, ad esempio Object.prototype o Array.prototype, verrà sempre restituito true.

Un altro modo per illustrare ciò è estrarre {} dalla funzione e assegnarlo a una variabile obj.

var obj = {}; 
function extendAndInstantiate(constructorFn) { 
    constructorFn.prototype = obj; 
    return new constructorFn(); 
} 

Ora questo sarà sempre tornare vero, perché non si sta creando un nuovo {} ogni volta che si esegue la funzione.

0

instanceof si basa sull'oggetto prototipo.

function extendAndInstantiate(constructorFn) { 
constructorFn.prototype = {}; //Can be any prototype 
... 

Quando si imposta il prototipo su {} si crea un nuovo oggetto e lo si imposta come prototipo. Poiché non è quindi lo stesso oggetto del prototipo, instanceof restituisce false.

Una configurazione come questa restituirà vero per tutti i casi, perché il prototipo sarà lo stesso oggetto:

function SomeConstructorFunction() { 
} 

var emptyObj = {}; 

function extendAndInstantiate(constructorFn) { 
    constructorFn.prototype = emptyObj; 
    return new constructorFn(); 
} 

var child1 = extendAndInstantiate(SomeConstructorFunction); 
console.log(child1 instanceof SomeConstructorFunction); //true 

var child2 = extendAndInstantiate(SomeConstructorFunction); 
console.log(child1 instanceof SomeConstructorFunction); //true 
console.log(child2 instanceof SomeConstructorFunction); //true 
0

Ogni volta che si chiama extendAndInstantiate un nuovo oggetto viene creato utilizzando {} e viene assegnato come il prototipo di SomeConstructorFunction. Ciò cancella il collegamento tra istanze esistenti e SomeConstructorFunction. Quindi solo la finale sarà un'istanza valida di SomeConstructorFunction.

function SomeConstructorFunction() { 

} 

function extendAndInstantiate(constructorFn) { 
    constructorFn.prototype = {}; //New instance as prototype 
    return new constructorFn(); 
} 

var child1 = extendAndInstantiate(SomeConstructorFunction); 
console.log(child1 instanceof SomeConstructorFunction); //true 

var child2 = extendAndInstantiate(SomeConstructorFunction); 
console.log(child1 instanceof SomeConstructorFunction); //false 
console.log(child2 instanceof SomeConstructorFunction); //true 


var child3 = extendAndInstantiate(SomeConstructorFunction); 
console.log(child1 instanceof SomeConstructorFunction); //false 
console.log(child2 instanceof SomeConstructorFunction); //false 
console.log(child3 instanceof SomeConstructorFunction); //true 

Quindi, o si può seguire il metodo mostrato da @ ben336 o fare un controllo di condizione prima di assegnare prototipo come illustrato di seguito.

function Base(){ 

} 

function SomeConstructorFunction() { 

} 

function extendAndInstantiate(constructorFn, Base) { 
    if(!(constructorFn.prototype instanceof Base)){ 
    console.log(" extend only once "); 
    constructorFn.prototype = new Base(); 
    } 
    return new constructorFn(); 
} 


var child1 = extendAndInstantiate(SomeConstructorFunction, Base); 
console.log(child1 instanceof SomeConstructorFunction); //true 

var child2 = extendAndInstantiate(SomeConstructorFunction, Base); 
console.log(child1 instanceof SomeConstructorFunction); //true 
console.log(child2 instanceof SomeConstructorFunction); //true 


var child3 = extendAndInstantiate(SomeConstructorFunction, Base); 
console.log(child1 instanceof SomeConstructorFunction); //true 
console.log(child2 instanceof SomeConstructorFunction); //true 
console.log(child3 instanceof SomeConstructorFunction); //true 
Problemi correlati