instanceof 检查在不设置构造函数的情况下对子类起作用

instanceof check works on subclass without setting constructor

我有以下 JavaScript 代码

function Parent() {
}

function Child() {
}

Child.prototype = Object.create(Parent.prototype);

注意没有声明

Child.prototype.constructor = Child;

我的理解是,由于尚未设置 constructor 属性,instanceof 检查应该会失败 Child class 的新实例。

var child = new Child();
child instanceof Child; //Should give false

我确认构造函数设置不正确

但是当我 运行 child instanceof Child 它给了我 true

但它应该是 false,因为构造函数 属性 没有在 Child 的原型上设置为 Child

环境

Google Chrome Version 48.0.2564.109 (64-bit)
Ubuntu 14.04 LTS

My understanding is that as the constructor property has not been set the instanceof checks should fail for new instances of Child class.

constructor 属性 根本没有被 instanceof 使用。事实上,直到 ES2015(又名 ES6),constructor 属性 并未用于 JavaScript 本身的 anything。它被定义存在于运行时分配给prototype属性函数的默认对象上,但未使用 .

让我们看看 instanceof 是如何工作的。考虑:

o instanceof Foo

从 ES2015 开始,instanceof 将检查 Foo 是否实现了一个名为 @@hasInstance 的内部操作,如果是,它将使用该操作来询问 Foo 是否o是一个实例。

在 ES5 及更早版本中(或者如果 Foo 没有该内部操作),instanceof 将查看 Foo.prototype 指向的对象是否在 [= 上的任何位置25=]的原型链。 (如果“原型链”不是一个熟悉的术语,请查看答案末尾的 * 然后回来。)如果是这样,它 returns true;如果没有,它 returns false.

例如,这里是 instanceof 的概念性实现,省略了一些细节:

function isAnInstance(obj, func) {
    // Start: ES2015+ part
    const hasInstance = func[Symbol.hasInstance];
    if (hasInstance) {
        return hasInstance.call(func, obj);
    }
    // End: ES2015+ part
    // Start: The OrdinaryHasInstance specification operation
    for (let p = Object.getPrototypeOf(obj); p; p = Object.getPrototypeOf(p)) {
        if (p === func.prototype) {
            return true;
        }
    }
    return false;
    // End: The OrdinaryHasInstance specification operation
}

JavaScript do中内置的函数有@@hasInstance(它们继承自Funtion),但它所做的只是OrdinaryHasInstance操作如上所示。请参阅规范 here, here, and here

我们可以从您的问题以及这个简单的演示中看出 constructor 没有涉及:

var p = {};
var o = Object.create(p);
var Foo = function() {};
Foo.prototype = p;
console.log(o instanceof Foo);                // true
console.log(o.hasOwnProperty("constructor")); // false
console.log(
    Object.getPrototypeOf(o).constructor === Object
);                                            // true

注意:

  1. o 不是通过 Foo 创建的。
  2. 事实上,Foo甚至不存在直到o被创建。
  3. o 没有自己的 constructor 属性.
  4. o的原型constructor属性是Object,不是Foo.

...然而 instanceof 说“是的,看起来它是一个 Foo。” :-) 纯粹是因为 Foo.prototype 指向的对象也在 o 的原型链上。


* "原型链"

您清楚地知道 JavaScript 中的对象具有从中继承属性的原型。这些原型是对象,因此 它们 有原型。所以你得到了原型的“链”。

考虑一个两级 (可以说是三级) 继承层次结构,在 ES5 中:

function Base() {
}

function Derived() {
    Base.call(this);
}
Derived.prototype = Object.create(Base.prototype);
Derived.prototype.constructor = Derived;

...或者在 ES2015 中:

class Base {
}
class Derived extends Base {
}

现在我们使用它:

var d = new Derived();

(在下面看到“d 对象”和类似内容的地方,我的意思当然是“d 对象指的是”——但这真的很冗长.)

现在,d 对象的原型是 Derived.prototypeDerived.prototype的原型是Base.prototypeBase.prototype的原型是Object.prototypeObject.prototype 没有原型(它的 [[Prototype]] 内部插槽是 null)。

这些对象是d下的原型链,它们意味着dinstanceof Objectinstanceof Base,和 instanceof Derived.