使用 ES6 语法和 Babel 扩展 Javascript 中的错误

IT技术 javascript ecmascript-6 babeljs transpiler
2021-02-14 23:05:55

我正在尝试使用 ES6 和 Babel 扩展 Error。它行不通。

class MyError extends Error {
  constructor(m) {
    super(m);
  }
}

var error = new Error("ll");
var myerror = new MyError("ll");
console.log(error.message) //shows up correctly
console.log(myerror.message) //shows empty string

Error 对象永远不会得到正确的消息集。

尝试使用 Babel REPL

现在我已经在 SO 上看到了一些解决方案(例如这里),但它们看起来都非常不符合 ES6 标准。如何以一种不错的 ES6 方式做到这一点?(那是在 Babel 中工作的)

6个回答

根据 Karel Bílek 的回答,我会对以下内容进行一些小改动constructor

class ExtendableError extends Error {
  constructor(message) {
    super(message);
    this.name = this.constructor.name;
    if (typeof Error.captureStackTrace === 'function') {
      Error.captureStackTrace(this, this.constructor);
    } else { 
      this.stack = (new Error(message)).stack; 
    }
  }
}    

// now I can extend

class MyError extends ExtendableError {}

var myerror = new MyError("ll");
console.log(myerror.message);
console.log(myerror instanceof Error);
console.log(myerror.name);
console.log(myerror.stack);

这将打印MyError在堆栈中,而不是通用Error.

它还会将错误消息添加到堆栈跟踪中——这是 Karel 的示例中缺少的。

captureStackTrace如果可用,它也会使用

使用 Babel 6,您需要transform-b​​uiltin-extend ( npm ) 才能使其工作。

这在 Babel 6 中不起作用: new MyError('foo') instanceof MyError === false
2021-04-22 23:05:55
此代码使用 babel 作为 NPM module进行预编译:extendable-error-class npmjs.com/package/extendable-error-class可以方便地避免对 babel-plugin-transform-b​​uiltin-extend 的依赖
2021-04-26 23:05:55
@迈克尔扬金if (typeof Error.captureStackTrace === 'function') { Error.captureStackTrace(this, this.constructor.name) } else { this.stack = (new Error(message)).stack; } 我认为最好使用此函数(如果它可用),因为它提供了更“本机”的调用堆栈并打印错误对象的名称。当然,如果您也仅在服务器端(Node)上使用它,那么这也不是问题。
2021-04-27 23:05:55
@MichaelYounkin 我认为这不值得投反对票。OP 谈到了在 ES6 中扩展错误。按照这个逻辑,至少有一个浏览器缺少几乎所有的 ES6。我的解决方案(添加了 func 检查)在最广泛使用的浏览器中提供本机覆盖,在其他浏览器中提供回退,在 Node.js 中提供 100% 覆盖。我同意,如果你一贯的错误类名然后this.stack = (new Error(message)).stack让你......但实际上,这可能不是什么大问题。
2021-05-07 23:05:55
this.message = message; 是多余的 super(message);
2021-05-07 23:05:55

结合这个答案这个答案这个代码,我制作了这个小“助手”类,它似乎工作正常。

class ExtendableError extends Error {
  constructor(message) {
    super();
    this.message = message; 
    this.stack = (new Error()).stack;
    this.name = this.constructor.name;
  }
}    

// now I can extend

class MyError extends ExtendableError {
  constructor(m) {   
    super(m);
  }
}

var myerror = new MyError("ll");
console.log(myerror.message);
console.log(myerror instanceof Error);
console.log(myerror.name);
console.log(myerror.stack);

在 REPL 中尝试

myerror.name现在返回“错误”。不确定这是否与 babel 的更高版本有关。请参阅下面的@sukima 的回答
2021-04-20 23:05:55
我怀疑这不能按需要工作,因为如果你这样做: console.log(myerror instanceof ExtendableError); 还是说假的。。
2021-04-29 23:05:55
同样的问题,使用 instanceof CustomError 不起作用,如果您不能使用 instanceof,那么扩展有什么意义。
2021-05-03 23:05:55
this.stack = (new Error(message)).stack; -- 否则堆栈跟踪中缺少消息
2021-05-08 23:05:55
可以改进message在 Error 堆栈构造函数中添加 ,因此它在抛出时在堆栈顶部显示正确的消息:this.stack = (new Error(message)).stack;
2021-05-15 23:05:55

终于把这件事平息了。在通天六是明确的,开发商不支持从内置的扩展。虽然这一招不会的东西帮助像MapSet等它确实工作Error这很重要,因为可以抛出异常的语言的核心思想之一是允许自定义错误。随着 Promise 变得更加有用,这变得更加重要,因为它们旨在拒绝 Error

可悲的事实是您仍然需要在 ES2015 中以旧方式执行此操作。

Babel REPL 中的示例

自定义错误模式

class MyError {
  constructor(message) {
    this.name = 'MyError';
    this.message = message;
    this.stack = new Error().stack; // Optional
  }
}
MyError.prototype = Object.create(Error.prototype);

另一方面,Babel 6 有一个插件可以实现这一点。

https://www.npmjs.com/package/babel-plugin-transform-b​​uiltin-extend

更新:(截至 2016-09-29)经过一些测试,babel.io 似乎没有正确解释所有断言(从自定义扩展错误扩展)。但在 Ember.JS 中,扩展错误按预期工作:https ://ember-twiddle.com/d88555a6f408174df0a4c8e0fd6b27ce

出于好奇,如果 ES2016 规范说内置函数是可扩展的,为什么像 v8 和 Babel 的 es5 这样的 vm 会如此反对它?一个类可以像原型链来自其他原型一样扩展一个类,这难道不是一个合理的期望吗?为什么需要隐藏在插件中的这种仪式?
2021-04-16 23:05:55
当大多数用例只想制作共享行为的简单对象时,这尤其令人沮丧。可以使用的自定义错误Error.toString()需要做特殊的箍环和旋转来完成这意味着大多数开发人员将避免它并诉诸不良做法,例如抛出字符串而不是错误。或者制作自己的 Map 之类的对象。为什么需要阻止这种 OOP 方法?
2021-04-18 23:05:55
是的,使用链接的 babel 插件,它可以与接受的答案一起正常工作。(但是,生成的文件在 Node 中不起作用,因为它显然没有 Reflect)
2021-05-01 23:05:55
到目前为止,关于该主题的模拟问题的所有回答都留在了“babel 不支持那个”,我认为这就是对话的结束。我的缺点是缺乏支持使常见的 OOP 习语变得困难,我什至不得不与同事斗争以让他们摆脱样板的束缚。我只是希望这里有一个干净的替代解决方法。看来添加插件是最好的选择。
2021-05-04 23:05:55
在我看来,他们并不反对,这只是一些技术问题。我不确定!你可以问问他们:) 项目很开放
2021-05-08 23:05:55

编辑Typescript 2.1中的重大更改

扩展 Error、Array 和 Map 等内置函数可能不再有效。

作为建议,您可以在任何 super(...) 调用后立即手动调整原型。

稍微编辑 Lee Benson 的原始答案对我有用。这还会向实例添加类的stack其他方法ExtendableError

class ExtendableError extends Error {
   constructor(message) {
       super(message);
       Object.setPrototypeOf(this, ExtendableError.prototype);
       this.name = this.constructor.name;
   }
   
   dump() {
       return { message: this.message, stack: this.stack }
   }
 }    

class MyError extends ExtendableError {
    constructor(message) {
        super(message);
        Object.setPrototypeOf(this, MyError.prototype);
    }
}

var myerror = new MyError("ll");
console.log(myerror.message);
console.log(myerror.dump());
console.log(myerror instanceof Error);
console.log(myerror.name);
console.log(myerror.stack);
2021-05-04 23:05:55

随着 babel 6 的最新变化,我发现transform-b​​uiltin-extend不再有效。我最终使用了这种混合方法:

export default class MyError {
    constructor (message) {
        this.name = this.constructor.name;
        this.message = message;
        this.stack = (new Error(message)).stack;
    }
}

MyError.prototype = Object.create(Error.prototype);
MyError.prototype.constructor = MyError;

import MyError from './MyError';

export default class MyChildError extends MyError {
    constructor (message) {
        super(message);
    }
}

结果所有这些测试都通过了:

const sut = new MyError('error message');
expect(sut.message).toBe('error message');
expect(sut).toBeInstanceOf(Error);
expect(sut).toBeInstanceOf(MyError);
expect(sut.name).toBe('MyError');
expect(typeof sut.stack).toBe('string');

const sut = new MyChildError('error message');
expect(sut.message).toBe('error message');
expect(sut).toBeInstanceOf(Error);
expect(sut).toBeInstanceOf(MyError);
expect(sut).toBeInstanceOf(MyChildError);
expect(sut.name).toBe('MyChildError');
expect(typeof sut.stack).toBe('string');