【问题标题】:Why doesn't instanceof work on instances of Error subclasses under babel-node?为什么 instanceof 对 babel-node 下的 Error 子类的实例不起作用?
【发布时间】:2016-02-25 12:54:56
【问题描述】:

我看到instanceof 运算符在Error 子类的实例上不起作用,当在 OS X 上的babel-node 版本 6.1.18/节点版本 5.1.0 下运行时。这是为什么?相同的代码在浏览器中运行良好,以我的fiddle 为例。

以下代码在浏览器中输出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));

【问题讨论】:

  • 我无法重现您的问题。我使用在线 babel repl 和使用 babel-node(最新)进行了测试。
  • 你在 Babel 中使用了什么预设?也许发布转换后的脚本将有助于重现问题
  • @DenysSéguret 谢谢,会进一步检查。
  • @RGraham 小提琴在浏览器中使用 Babel?关键是 Babel 在浏览器和 Node 中的工作方式显然不同。
  • @RGraham 我已经发布了编译好的脚本。

标签: javascript node.js ecmascript-6 babeljs


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    tl;dr 如果你在 Babel 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,它的行为可能有点像数组,并且有数组方法,但它的行为并不完全像数组。

    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-builtin-extend。其中的approximate 选项与Babel 5 中的行为相同。请注意,非approximate 行为绝对是极端情况,可能无法在100% 的情况下工作。

    【讨论】:

    • 啊,谢谢。那么创建新异常类的推荐方法是什么??
    • 毕竟,MDN 的recommends 原型继承了Error 来创建新的异常类。 AFAICT,这应该对应于 ES6 中的子类化,除非我忽略了某些东西。我还在 ES6 类语义上找到了 this article,其中有一个子类化 Error 的示例。
    • 我扩大了我的答案。 ES6 中的子类化比你想象的要复杂得多,并且不能很好地映射到 ES5。
    • 修复了吗?或者它会被修复吗?他们在 phabricator 中为这个问题添加了Wontfix 标签,这有点令人失望,因为我认为我应该期望这个功能能够工作,至少如果 Babel 做得对并且符合规范的话。
    • 对于这些用例,我只是在 ES5 中编写它们,例如var CustomError = function(message){ Error.call(this, message) this.message = message }
    猜你喜欢
    • 2014-07-24
    • 2016-10-10
    • 1970-01-01
    • 2016-04-05
    • 2017-02-12
    • 1970-01-01
    • 2018-05-27
    相关资源
    最近更新 更多