为什么 instanceof 不能在 babel-node 下的 Error 子类的实例上工作?

我发现当在 OS X 上的 Babel-node版本6.1.18/Node 版本5.1.0下运行时,instanceof操作符不能在 Error子类的实例上工作。为什么会这样?同样的代码在浏览器中运行良好,可以尝试使用我的 小提琴作为示例。

下面的代码在浏览器中输出 true,而在 babel-node 中输出的是 false:

class Sub extends Error {
}


let s = new Sub()
console.log(`The variable 's' is an instance of Sub: ${s instanceof Sub}`)

我只能想象这是由于 babel-node 中的一个错误造成的,因为 instanceof适用于 Error以外的其他基类。

。巴贝尔

{
"presets": ["es2015"]
}

已编译输出

这是由 babel 6.1.18编译的 JavaScript:

'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 Sub = (function (_Error) {
_inherits(Sub, _Error);


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


return _possibleConstructorReturn(this, Object.getPrototypeOf(Sub).apply(this, arguments));
}


return Sub;
})(Error);


var s = new Sub();
console.log('The variable \'s\' is an instance of Sub: ' + (s instanceof Sub));
20744 次浏览

如果你在巴别塔6上,你可以使用 https://www.npmjs.com/package/babel-plugin-transform-builtin-extend

扩展内置类型,如 ArrayError等,从来没有在 Babel 支持。它在真正的 ES6环境中是完全有效的,但是有一些要求使它能够工作,而这些要求很难以与较老的浏览器兼容的方式传输。它在 Babel 5中“工作”了,因为它没有抛出错误,但是从扩展子类实例化的对象没有像它们应该的那样工作,例如:

class MyError extends Error {}


var e1 = new MyError();
var e2 = new Error();


console.log('e1', 'stack' in e1);
console.log('e2', 'stack' in e2);

结果出来了

e1 false
e2 true

虽然它没有出错,但是子类没有正确地得到一个“堆栈”,就像错误应该得到的那样。类似地,如果要扩展 Array,它的行为可能有点像数组,有数组方法,但它的行为不完全像数组。

Babel5文档特别指出这是需要注意的类的边缘情况。

在 Babel 6中,类在子类的处理方式上更符合规范,这样做的一个副作用是,现在上面的代码将 还是不能工作,但它不会以与以前不同的方式工作。这在 https://phabricator.babeljs.io/T3083中已经讨论过了,但是我将在这里详细介绍一个潜在的解决方案。

要返回 Babel 5子类化行为(记住,仍然不正确或不推荐) ,您可以将内置构造函数包装在您自己的临时类中,例如。

function ExtendableBuiltin(cls){
function ExtendableBuiltin(){
cls.apply(this, arguments);
}
ExtendableBuiltin.prototype = Object.create(cls.prototype);
Object.setPrototypeOf(ExtendableBuiltin, cls);


return ExtendableBuiltin;
}

用这个帮手,而不是做

class MyError extends Error {}

class MyError extends ExtendableBuiltin(Error) {}

然而,在您的特定情况下,您说过您是在 Node 5.x 上。节点5支持本机 ES6类,而不需要传输。我建议您通过删除 es2015预设值来使用它们,而是使用 node5来获得本机类。在这种情况下,

class MyError extends Error {}

会如你所愿。

对于不使用 Node 4/5或最近的 Chrome 浏览器的用户,你可以考虑使用类似于 https://www.npmjs.com/package/error的软件。您还可以探索 https://www.npmjs.com/package/babel-plugin-transform-builtin-extendapproximate选项与 Babel5的行为相同。请注意,非 approximate行为绝对是边缘性的,可能不会在100% 的情况下工作。

如果编译目标设置为“ es5”,则对于子类错误,instanceof将不起作用。我在 tsconfig.json中将目标设置为“ es6”,而 instanceof产生了正确的结果。