instanceof 使用 ES6 class 继承链不起作用

instanceof using ES6 class Inheritance chain doesn't work

使用 ES6 class 语法,我想知道当有多个继承链时,为什么 instanceof 运算符对继承链不起作用?

(optional read)

How instanceof operator works?

In obj instanceof Constructor, the instanceof operator checks if the 'prototype' property of the Constructor function is present in the prototype chain of the obj. If it is present, return true. Otherwise, false.


在下面的代码片段中,BTError 继承自 Error (1.) 和 SomeError 扩展自 BTError (3.).
但是正如我们从 (4. 中看到的那样,instanceof 运算符结果 false for new SomeError() instanceof BTError在我的理解中应该是 true.

class BTError extends Error {}
console.log('1.', Reflect.getPrototypeOf(BTError.prototype) === Error.prototype); // 1. true
console.log('2.', new BTError() instanceof Error); // 2. true

console.log('');

class SomeError extends BTError {}
console.log('3.', Reflect.getPrototypeOf(SomeError.prototype) === BTError.prototype); // 3. true
console.log('4.', new SomeError() instanceof BTError); // 4. false

console.log('');

class SpecificError extends SomeError {}
console.log('5.', Reflect.getPrototypeOf(SpecificError.prototype) === SomeError.prototype); // 5. true
console.log('6.', new SpecificError() instanceof Error); // 6. true
console.log('7.', new SpecificError() instanceof BTError); // 7. false
console.log('8.', new SpecificError() instanceof SomeError); // 8. false


问题

是否有任何我无法理解的重要事情,或者 instanceof 操作员只是在表现 怪异

关注示例的最后一部分

您正在使用 BabelJS 转换此代码以使其兼容

class BTError extends Error {}
class SomeError extends BTError {}
class SpecificError extends SomeError {}

console.log('6.', new SpecificError() instanceof Error);
console.log('7.', new SpecificError() instanceof BTError);
console.log('8.', new SpecificError() instanceof SomeError);

这是上面代码的转译版本

'use strict';

function _classCallCheck(instance, Constructor) {
    if (!(instance instanceof Constructor)) {
        throw new TypeError("Cannot call a class as a function");
    }
}

function _possibleConstructorReturn(self, call) {
    if (!self) {
        throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
    }
    return call && (typeof call === "object" || typeof call === "function") ? call : self;
}

function _inherits(subClass, superClass) {
    if (typeof superClass !== "function" && superClass !== null) {
        throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
    }
    subClass.prototype = Object.create(superClass && superClass.prototype, {
        constructor: {
            value: subClass,
            enumerable: false,
            writable: true,
            configurable: true
        }
    });
    if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}

var BTError = function(_Error) {
    _inherits(BTError, _Error);

    function BTError() {
        _classCallCheck(this, BTError);

        return _possibleConstructorReturn(this, (BTError.__proto__ || Object.getPrototypeOf(BTError)).apply(this, arguments));
    }

    return BTError;
}(Error);

var SomeError = function(_BTError) {
    _inherits(SomeError, _BTError);

    function SomeError() {
        _classCallCheck(this, SomeError);

        return _possibleConstructorReturn(this, (SomeError.__proto__ || Object.getPrototypeOf(SomeError)).apply(this, arguments));
    }

    return SomeError;
}(BTError);

var SpecificError = function(_SomeError) {
    _inherits(SpecificError, _SomeError);

    function SpecificError() {
        _classCallCheck(this, SpecificError);

        return _possibleConstructorReturn(this, (SpecificError.__proto__ || Object.getPrototypeOf(SpecificError)).apply(this, arguments));
    }

    return SpecificError;
}(SomeError);

console.log('6.', new SpecificError() instanceof Error); // 6. true
console.log('7.', new SpecificError() instanceof BTError); // 7. false
console.log('8.', new SpecificError() instanceof SomeError); // 8. false

我认为问题出在 _inherit 方法,该方法分配给 subClass.prototype 而不是直接分配给 superClass.prototype,而是通过合并该对象和另一组默认属性创建的对象。

有了这个原型链,继承将起作用,但 instanceof 运算符将无法通过引用遍历它,所以你得到 false 你期望的 true.

显然,根据 this bug report, it's a known and expected behaviour (ie. limitation) and a possible workaround is to use babel-plugin-transform-builtin-extend

TypeScript 有这个问题。通过在 super 调用后的 class 构造函数中添加以下内容解决了这个问题:

Object.setPrototypeOf(this, YOUR_CLASS_HERE.prototype);

不确定是否对您有帮助。

在我的例子中,instanceof 无法使用设置为“es5”的编译目标。当更改为“es6”时,它产生了正确的结果。