2013-03-19 60 views
5

在構造函數上設置原型時,instanceof操作符只會返回true,直到原型發生更改。爲什麼?instanceof操作符對繼承鏈的後續更改返回false

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 

回答

3

原型遺傳可能有點大腦彎曲。我已經在下面的答案中爲原型繼承寫了一個簡單的解釋,我建議你閱讀它:https://stackoverflow.com/a/8096017/783743

現在回答你的問題。考慮下面的函數:

function F() {} 

如下我可以創建使用new一個的F實例:

var f = new F; 

正如預期的那樣f instanceof F回報true。這是因爲instanceof運營商在f的原型鏈中檢查F.prototype,如果它在那裏返回true。請參閱我上面鏈接的答案。

現在說我創建一個新的功能G如下,並設置F.prototypeG.prototype

function G() {} 

F.prototype = G.prototype; 

如果我現在評價f instanceof F再次返回false。這是因爲F.prototype不再位於f的原型鏈中(請記住F.prototype現在是G.prototype)。

現在讓我們創建的F一個新實例:

var g = new F; 

如果您評估g instanceof G它會返回true即使g = new F。這是因爲G.prototype存在於g的原型鏈中。

這不是一個錯誤。這是JavaScript的工作方式。事實上,我們可以利用這個功能來創建一些非常有趣的功能:Instantiate JavaScript functions with custom prototypes

+0

對原型繼承的很好的解釋。我認爲OP的困惑在於'constructorFn.prototype = {}; //可以是任何原型。他期望將原型設置爲相同的**值**將導致'instanceof'爲true,相反,它們應該設置爲相同的**引用**。 – MattDiamant 2013-03-19 15:32:57

+0

@MattDiamant - 我同意。我不知道OP使用'extendAndInstantiate'試圖完成什麼,但我相信它有點像我自己的'instantiate'函數在下面的問題:http://stackoverflow.com/q/11490606/783743 – 2013-03-19 15:36:28

+0

好解釋。最重要的是,我錯過了將原型設置爲'{}'有效地將'SomeConstructorFunction'類型更改爲'Object'的事實。 'SomeConstructorFunction.constructor'是'function Object ...'。 – Stephen 2013-03-19 17:07:24

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

事實並非如此!

constructorFn.prototype = Object.prototype; 

或者任何其他原生原型將使它們全部成真。

原因是,您第一次打電話extendAndInstantiate()時,您將SomeConstructorFunction的原型設置爲某個東西(這裏是一個空對象{})。對於child1,instanceOf只會返回true,而SomeConstructorFunction的原型就是{}的確切實例。當您運行extendAnInstantiate()第二次,你改變SomeConstructorFunction的原型的{}不同的實例,所以child2將是一個instanceof新{},但child1仍是老{}一個instanceof,所以child1將返回false,而child2返回true。如果將原型設置爲普通原型,例如Object.prototypeArray.prototype,它將始終返回true。

另一種說明此方法的方法是將{}拉出函數,並將其分配給變量obj

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

現在,這將始終返回true,因爲你沒有創建每次運行函數時新{}

0

instanceof基於原型對象。

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

當您將原型設置爲{}時,您將創建一個新對象並將其設置爲原型。因爲它與原型不同,因此instanceof返回false。

像這樣的設置會爲所有的情況下返回true,因爲原型將是相同的對象:

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

每次調用extendAndInstantiate一個新的對象是使用{}創建,它被指定爲原型時間的SomeConstructorFunction。這將清除現有實例和SomeConstructorFunction之間的鏈接。所以只有最後一個纔是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 

因此,要麼你可以按照法顯示由@ ben336或如下所示指定原型之前做健康檢查。

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