instanceof运算符在后续更改为继承链时返回false

时间:2013-03-19 14:36:37

标签: javascript prototype

在构造函数上设置原型时,instanceof运算符仅返回true,直到原型发生变化。为什么呢?

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

4 个答案:

答案 0 :(得分:3)

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

不正确!

constructorFn.prototype = Object.prototype;

或者任何其他原生原型都会使它们都成立。

原因是您第一次调用extendAndInstantiate()时,您将SomeConstructorFunction的原型设置为某个(此处为空对象{})。对于child1,instanceOf将仅返回true,而SomeConstructorFunction的原型{} 的确切实例。当您再次运行extendAnInstantiate()时,您将SomeConstructorFunction的原型更改为{}的其他实例,因此child2将成为instanceOf新的{},但child1仍然是旧{}的实例,因此child1将返回false,而child2将返回true。如果您将原型设置为常见原型(例如Object.prototypeArray.prototype),则它将始终返回true。

另一种说明方法是将{}拉出函数,并将其分配给变量obj

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

现在这将永远返回true,因为每次运行该函数时都不会创建新的{}

答案 1 :(得分:3)

原型继承可能有点大脑弯曲。我已经在下面的答案中为原型继承写了一个简单的解释,我建议你阅读它:https://stackoverflow.com/a/8096017/783743

现在回答你的问题。考虑以下功能:

function F() {}

我可以使用F创建new的实例,如下所示:

var f = new F;

按预期f instanceof F返回true。这是因为instanceof运算符会在F.prototype的原型链中检查f,如果存在,则返回true。请参阅我上面链接的答案。

现在说我创建了一个新功能G,如下所示,并将F.prototype设置为G.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

答案 2 :(得分: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

答案 3 :(得分: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