扩展错误没有消息或堆栈跟踪

ssu*_*ube 12 javascript ecmascript-6 babeljs

通过BabelJS运行此代码段时:

class FooError extends Error {
  constructor(message) {
    super(message);
  }
}

let error = new FooError('foo');
console.log(error, error.message, error.stack);
Run Code Online (Sandbox Code Playgroud)

它输出

{}
Run Code Online (Sandbox Code Playgroud)

这不是我的期望.运行

error = new Error('foo');
console.log(error, error.message, error.stack);
Run Code Online (Sandbox Code Playgroud)

产生

{} foo Error: foo
    at eval (eval at <anonymous> (https://babeljs.io/scripts/repl.js?t=2015-05-21T16:46:33+00:00:263:11), <anonymous>:24:9)
    at REPL.evaluate (https://babeljs.io/scripts/repl.js?t=2015-05-21T16:46:33+00:00:263:36)
    at REPL.compile (https://babeljs.io/scripts/repl.js?t=2015-05-21T16:46:33+00:00:210:12)
    at Array.onSourceChange (https://babeljs.io/scripts/repl.js?t=2015-05-21T16:46:33+00:00:288:12)
    at u (https://cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.min.js:28:185)
Run Code Online (Sandbox Code Playgroud)

这正是我想从扩展错误中得到的.

我的目标是扩展Error到各种子类,并在bluebird的catch匹配中使用它们.到目前为止,这是悲惨的失败.

为什么子类没有显示消息或堆栈跟踪?

编辑: 使用Chrome的内置子类(感谢@coder)完美运行.这并不是Babel特有的,因为下面的例子(来自Babel的gitter feed上的@loganfsmyth)显示:

// Works
new (function(){
  "use strict";
  return class E extends Error { }
}());
// Doesn't
new (function(){
  "use strict";
  function E(message){
    Error.call(this, message);
  };
  E.prototype = Object.create(Error);
  E.prototype.constructor = E;
  return E;
}());
Run Code Online (Sandbox Code Playgroud)

Mos*_*sho 10

简而言之,使用babel的转换代码扩展仅适用于以特定方式构建的类,并且许多本机内容似乎不像这样构建.Babel的文档警告说,扩展许多本地类不能正常工作.

您可以创建一个"手动"创建属性的缓冲类,如下所示:

class ErrorClass extends Error {
  constructor (message) {
    super();

    if (Error.hasOwnProperty('captureStackTrace'))
        Error.captureStackTrace(this, this.constructor);
    else
       Object.defineProperty(this, 'stack', {
          value: (new Error()).stack
      });

    Object.defineProperty(this, 'message', {
      value: message
    });
  }

}
Run Code Online (Sandbox Code Playgroud)

然后扩展该类:

class FooError extends ErrorClass {
  constructor(message) {
    super(message);
  }
}
Run Code Online (Sandbox Code Playgroud)

为什么它不像你期望的那样工作?

如果你看一下被转换的内容,你会看到babel首先将超类'原型的副本分配给子类,然后当你调用new SubClass()这个函数时调用:

_get(Object.getPrototypeOf(FooError.prototype), "constructor", this).call(this, message)
Run Code Online (Sandbox Code Playgroud)

其中_get是一个注入脚本的辅助函数:

(function get(object, property, receiver) {
  var desc = Object.getOwnPropertyDescriptor(object, property);

  if (desc === undefined) {
    var parent = Object.getPrototypeOf(object);

    if (parent === null) {
      return undefined;
    } else {
      return get(parent, property, receiver);
    }
  } else if ("value" in desc) {
    return desc.value;
  } else {
    var getter = desc.get;

    if (getter === undefined) {
      return undefined;
    }

    return getter.call(receiver);
  }
});
Run Code Online (Sandbox Code Playgroud)

它确实找到constructor子类'prototype的原型的属性描述符,并试图用新的子类实例作为上下文调用它的getter,如果它存在或返回它的value(if ("value" in desc)),在这种情况下是Error构造函数本身.它不会this从超级调用中分配任何内容,因此当新对象具有正确的原型时,它不会按照您期望的方式构建.基本上,超级调用对新构造的对象没有任何作用,只需创建一个Error未分配给任何东西的新对象.

如果我们使用ErrorClass上面定义的,它确实遵循Babel所期望的类结构.