为什么 instanceof 对 babel-node 下的 Error 子类的实例不起作用?

IT技术 javascript node.js ecmascript-6 babeljs
2021-02-24 05:40:39

在 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.

.babelrc

{
  "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));
2个回答

tl;dr 如果您使用的是 Babel 6,则可以使用https://www.npmjs.com/package/babel-plugin-transform-b​​uiltin-extend

Babel 从未支持扩展诸如Arrayand 之Error类的内置类型它在真实的 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它,它的行为可能有点像数组,并且具有数组方法,但它的行为并不完全像数组。

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

在 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。Node 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-b​​uiltin-extendapproximate来自 Babel 5选项与 Babel 5 的行为相同。请注意,非approximate行为绝对是边缘案例,可能无法在 100% 的情况下工作。

已经修好了吗?或者它会被修复吗?他们Wontfix在 phabricator 中标记了这个问题,这有点令人失望,因为我认为我应该期望这个功能能够工作,至少如果 Babel 做得对并且符合规范。
2021-04-19 05:40:39
对于这些用例,我刚刚在 ES5 中编写它们,例如 var CustomError = function(message){ Error.call(this, message) this.message = message }
2021-04-20 05:40:39
毕竟,MDN建议通过原型继承Error来创建新的异常类。AFAICT,这应该对应于 ES6 中的子类化,除非我忽略了一些东西。我还发现了这篇关于 ES6 类语义的文章,其中有一个关于 sublassing 的例子Error
2021-04-25 05:40:39
啊哈谢谢。那么创建新异常类的推荐方法是什么?
2021-05-11 05:40:39
我稍微扩展了我的答案。ES6 中的子类化比您预期的要复杂得多,并且不能很好地映射到 ES5。
2021-05-11 05:40:39

instanceof如果编译目标设置为“es5”,则不适用于子类错误。我将目标设置为“es6”tsconfig.jsoninstanceof产生了正确的结果。