5

在构造函数上设置原型时,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

4 回答 4

3

Prototypal inheritance can be a bit of a brain bender. I've already written a simple explanation for prototypal inheritance in the following answer and I suggest you read it: https://stackoverflow.com/a/8096017/783743

Now to answer your question. Consider the following function:

function F() {}

I can create an instance of F using new as follows:

var f = new F;

As expected f instanceof F returns true. This is because the instanceof operator checks for F.prototype in the prototype chain of f and returns true if it is there. See the answer I linked to above.

Now say I create a new function G as follows and set F.prototype to G.prototype:

function G() {}

F.prototype = G.prototype;

If I now evaluate f instanceof F again false is returned. This is because F.prototype is no longer in the prototype chain of f (remember that F.prototype is now G.prototype).

Now let's create a new instance of F:

var g = new F;

If you evaluate g instanceof G it'll return true even though g = new F. This is because G.prototype exists in the prototype chain of g.

This is not a bug. It's the way JavaScript works. In fact we can exploit this feature to create some really interesting functions: Instantiate JavaScript functions with custom prototypes

于 2013-03-19T15:24:08.977 回答
3
constructorFn.prototype = {}; //Can be any prototype

不对!

constructorFn.prototype = Object.prototype;

或者任何其他本机原型都会使它们全部真实。

原因是您第一次调用时extendAndInstantiate(),将 SomeConstructorFunction 的原型设置为某个东西(这里是一个空对象{})。因为child1,instanceOf 只会返回 true,而 SomeConstructorFunction 的原型是 的确切实例{}。当您extendAnInstantiate()第二次运行时,您正在将SomeConstructorFunction的原型更改为 的不同实例{},因此child2将是那个 new 的 instanceOf {},但child1仍然是 old 的一个 instanceOf {},因此child1将返回 false,同时child2返回 true。如果将原型设置为通用原型,例如Object.prototypeor Array.prototype,它将始终返回 true。

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

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

现在这将始终返回 true,因为您不会在{}每次运行该函数时都创建一个新函数。

于 2013-03-19T15:03:02.377 回答
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
于 2013-03-19T15:18:12.137 回答
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
于 2013-03-19T15:08:48.250 回答