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

akn*_*ds1 73 javascript node.js ecmascript-6 babeljs

我看到instanceof操作符在OS X上的babel-node版本6.1.18/Node版本5.1.0 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}`)
Run Code Online (Sandbox Code Playgroud)

我只能想象这是由于babel-node中的一个bug,因为它instanceof适用于其他基类Error.

.babelrc

{
  "presets": ["es2015"]
}
Run Code Online (Sandbox Code Playgroud)

编译输出

这是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));
Run Code Online (Sandbox Code Playgroud)

log*_*yth 87

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

扩展内置类型,如ArrayError和这种从未在巴别被支持.它在真正的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);
Run Code Online (Sandbox Code Playgroud)

结果是

e1 false
e2 true
Run Code Online (Sandbox Code Playgroud)

虽然它没有错误输出,但是子类没有像错误一样正确地获得"堆栈".类似地,如果你要扩展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;
}
Run Code Online (Sandbox Code Playgroud)

有了这个助手,而不是做

class MyError extends Error {}
Run Code Online (Sandbox Code Playgroud)

class MyError extends ExtendableBuiltin(Error) {}
Run Code Online (Sandbox Code Playgroud)

但是,在您的具体情况下,您已经说过您使用的是Node 5.x. 节点5支持本机ES6类而无需转换.我建议您通过删除es2015预设来使用它们,而不是使用它,node5以便您获得本机类,以及其他内容.在这方面,

class MyError extends Error {}
Run Code Online (Sandbox Code Playgroud)

将以你期望的方式工作.

对于不在Node 4/5或最近的Chrome上的用户,您可能需要考虑使用https://www.npmjs.com/package/error之类的内容.您还可以浏览https://www.npmjs.com/package/babel-plugin-transform-b​​uiltin-extend.其中的approximate选项与Babel 5的行为相同.请注意,非approximate行为肯定是边缘情况,并且在100%的情况下可能无效.

  • 对于这些用例,我刚刚在 ES5 中编写了它们,例如 `var CustomError = function(message){ Error.call(this, message) this.message = message }` (3认同)
  • 毕竟,MDN [推荐](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error)原型继承`Error`来创建新的异常类.AFAICT,这应该对应于ES6中的子类,除非我忽略了某些东西.我还在ES6类语义上找到了[本文](http://www.2ality.com/2015/02/es6-classes-final.html),其中有一个完整的子类化"Error"的例子. (2认同)

bel*_*ref 8

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