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

Posted

技术标签:

【中文标题】使用 ES6 语法和 Babel 扩展 Javascript 中的错误【英文标题】:Extending Error in Javascript with ES6 syntax & Babel 【发布时间】:2015-09-14 09:19:53 【问题描述】:

我正在尝试使用 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 对象永远不会得到正确的消息集。

Try in Babel REPL.

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

【问题讨论】:

跟随您到 Babel REPL 的链接似乎表明它现在可以正常工作。我认为这是 Babel 中的一个错误,此后已修复。 【参考方案1】:

根据 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-builtin-extend (npm) 才能使其工作。

【讨论】:

@MichaelYounkin if (typeof Error.captureStackTrace === 'function') Error.captureStackTrace(this, this.constructor.name) else this.stack = (new Error(message)).stack; 。我认为如果这个函数可用的话最好使用它,因为它提供了一个更“本机”的调用堆栈并打印错误对象的名称。当然,如果你也只是在服务器端(Node)使用它,那也不是问题。 @MichaelYounkin 我认为这不值得一票。 OP 谈到了 ES6 中的扩展错误。按照这个逻辑,几乎所有的 ES6 至少在一个浏览器中缺失。我的解决方案(添加了 func 检查)在最广泛使用的浏览器中提供本机覆盖,在其他浏览器中提供回退,在 Node.js 中提供 100% 的覆盖。我同意,如果你的错误类名始终如一,那么this.stack = (new Error(message)).stack 会让你知道......但实际上,这可能不是什么大不了的事。 这在 Babel 6 中不起作用:new MyError('foo') instanceof MyError === false 这段代码预编译了 babel 作为 NPM 模块:extendable-error-class npmjs.com/package/extendable-error-class 方便避免对 babel-plugin-transform-b​​uiltin-extend 的依赖 this.message = message;super(message); 是多余的【参考方案2】:

结合this answer、this answer 和this code,我制作了这个小“助手”类,似乎工作正常。

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);

Try in REPL

【讨论】:

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

终于可以平息了。在 Babel 6 中,开发人员明确不支持从内置扩展。虽然这个技巧不会帮助处理MapSet 等问题。它确实适用于Error。这很重要,因为可以引发异常的语言的核心思想之一是允许自定义错误。这一点非常重要,因为 Promise 变得更加有用,因为它们被设计为 reject an Error。

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

Example in 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-builtin-extend

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

【讨论】:

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

编辑:Typescript 2.1中的重大更改

Error、Array 和 Map 等扩展内置函数可能不再起作用。

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

编辑 Lee Benson 的原始答案对我有用。这还将stackExtendableError 类的其他方法添加到实例中。

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);

【讨论】:

您还需要在MyError 构造函数中调用Object.setPrototypeOf。 ***.com/a/41102306/186334github.com/Microsoft/TypeScript-wiki/blob/master/…【参考方案5】:

随着 babel 6 的最新变化,我发现 transform-builtin-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');

【讨论】:

【参考方案6】:

Quoting

class MyError extends Error 
  constructor(message) 
    super(message);
    this.message = message;
    this.name = 'MyError';
  

感谢super() 调用,无需this.stack = (new Error()).stack; 技巧。

虽然上述代码无法输出堆栈跟踪,除非在Babel 中调用this.stack = (new Error()).stack;Error.captureStackTrace(this, this.constructor.name);。 IMO,这可能是这里的一个问题。

其实可以在Chrome consoleNode.js v4.2.1下用这段代码sn-ps输出堆栈跟踪。

class MyError extends Error
        constructor(msg) 
                super(msg);
                this.message = msg;
                this.name = 'MyError';
        
;

var myerr = new MyError("test");
console.log(myerr.stack);
console.log(myerr);

Chrome console 的输出。

MyError: test
    at MyError (<anonymous>:3:28)
    at <anonymous>:12:19
    at Object.InjectedScript._evaluateOn (<anonymous>:875:140)
    at Object.InjectedScript._evaluateAndWrap (<anonymous>:808:34)
    at Object.InjectedScript.evaluate (<anonymous>:664:21)

Node.js的输出

MyError: test
    at MyError (/home/bsadmin/test/test.js:5:8)
    at Object.<anonymous> (/home/bsadmin/test/test.js:11:13)
    at Module._compile (module.js:435:26)
    at Object.Module._extensions..js (module.js:442:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:311:12)
    at Function.Module.runMain (module.js:467:10)
    at startup (node.js:134:18)
    at node.js:961:3

【讨论】:

【参考方案7】:

除了@zangw 答案,您还可以像这样定义错误:

'use strict';

class UserError extends Error 
  constructor(msg) 
    super(msg);
    this.name = this.constructor.name;
  


// define errors
class MyError extends UserError 
class MyOtherError extends UserError 

console.log(new MyError instanceof Error); // true

throw new MyError('My message');

这将抛出正确的名称、消息和堆栈跟踪:

MyError: My message
    at UserError (/Users/honzicek/Projects/api/temp.js:5:10)
    at MyError (/Users/honzicek/Projects/api/temp.js:10:1)
    at Object.<anonymous> (/Users/honzicek/Projects/api/temp.js:14:7)
    at Module._compile (module.js:434:26)
    at Object.Module._extensions..js (module.js:452:10)
    at Module.load (module.js:355:32)
    at Function.Module._load (module.js:310:12)
    at Function.Module.runMain (module.js:475:10)
    at startup (node.js:117:18)
    at node.js:951:3

【讨论】:

这不起作用:new MyError('foo') instanceof MyError === false. 确实如此,Node.js v7.7.3【参考方案8】:

我正在尝试使用 ES6 扩展 Error

class MyError extends Error … 的语法是正确的。

请注意,转译器仍然存在从内置对象继承的问题。在你的情况下,

var err = super(m);
Object.assign(this, err);

似乎可以解决问题。

【讨论】:

真的!但是无论如何都没有设置消息 - 我会写一个新的例子。 我现在重写了例子 Doesn't work for me 显然,“super(m)”将返回一个空对象。所以 Object.assign 没有帮助。 @KarelBílek:你用的是什么浏览器? Error.call() 确实为我返回了一个新的错误实例。【参考方案9】:

鉴于此已接受的答案不再有效,您始终可以使用工厂作为替代方案 (repl):

function ErrorFactory(name) 
   return class AppError extends Error 
    constructor(message) 
      super(message);
      this.name = name;
      this.message = message; 
      if (typeof Error.captureStackTrace === 'function') 
        Error.captureStackTrace(this, this.constructor);
       else  
        this.stack = (new Error(message)).stack; 
      
    
       


// now I can extend
const MyError = ErrorFactory("MyError");


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

【讨论】:

如果你有必要的 babel 插件,接受的答案仍然对我有用。不过,也感谢您的回答!【参考方案10】:

我更喜欢比上面描述的更强大的语法。错误类型的其他方法将帮助您创建漂亮的console.log 或其他内容。

export class CustomError extends Error 
    /**
     * @param string message
     * @param number [code = 0]
     */
    constructor(message, code = 0) 
        super();

        /**
         * @type string
         * @readonly
         */
        this.message = message;

        /**
         * @type number
         * @readonly
         */
        this.code = code;

        /**
         * @type string
         * @readonly
         */
        this.name = this.constructor.name;

        /**
         * @type string
         * @readonly
         */
        this.stack = CustomError.createStack(this);
    

    /**
     * @return string
     */
    toString() 
        return this.getPrettyMessage();
    

    /**
     * @return string
     */
    getPrettyMessage() 
        return `$this.message Code: $this.code.`;
    

    /**
     * @param CustomError error
     * @return string
     * @private
     */
    static createStack(error) 
        return typeof Error.captureStackTrace === 'function'
            ? Error.captureStackTrace(error, error.constructor)
            : (new Error()).stack;
    

要测试此代码,您可以运行类似的代码:

try 
    throw new CustomError('Custom error was thrown!');
 catch (e) 
    const message = e.getPrettyMessage();

    console.warn(message);

欢迎扩展CustomError 类型。可以将某些特定功能添加到扩展类型或覆盖现有的。例如。

export class RequestError extends CustomError 
    /**
     * @param string message
     * @param string requestUrl
     * @param number [code = 0]
     */
    constructor(message, requestUrl, code = 0) 
        super(message, code);

        /**
         * @type string
         * @readonly
         */
        this.requestUrl = requestUrl;
    

    /**
     * @return string
     */
    getPrettyMessage() 
        const base = super.getPrettyMessage();

        return `$base Request URL: $this.requestUrl.`;
    

【讨论】:

【参考方案11】:

正如@sukima 提到的,你不能扩展原生 JS。 OP的问题无法回答。

类似于Melbourne2991's answer,我确实使用了工厂,但遵循了MDN's recommendation for customer error types。

function extendError(className)
  function CustomError(message)
    this.name = className;
    this.message = message;
    this.stack = new Error().stack; // Optional
  
  CustomError.prototype = Object.create(Error.prototype);
  CustomError.prototype.constructor = CustomError;
  return CustomError;

【讨论】:

【参考方案12】:

这对我有用:

/**
 * @class AuthorizationError
 * @extends Error
 */
export class AuthorizationError extends Error 
    message = 'UNAUTHORIZED';
    name = 'AuthorizationError';

【讨论】:

【参考方案13】:

不使用 Babel,但在普通的 ES6 中,以下对我来说似乎工作正常:

class CustomError extends Error 
    constructor(...args) 
        super(...args);
        this.name = this.constructor.name;
    

从 REPL 进行测试:

> const ce = new CustomError('foobar');
> ce.name
'CustomError'
> ce.message
'foobar'
> ce instanceof CustomError
true
> ce.stack
'CustomError: foobar\n    at CustomError (repl:3:1)\n ...'

如您所见,堆栈包含错误名称和消息。我不确定我是否遗漏了什么,但所有其他答案似乎都过于复杂了。

【讨论】:

【参考方案14】:

我通过这种方式改进了@Lee Benson 的解决方案:

extendableError.js

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




export default ExtendableError

错误示例

import ExtendableError from './ExtendableError'

const AuthorizationErrors = 
    NOT_AUTHORIZED: 401,
    BAD_PROFILE_TYPE: 402,
    ROLE_NOT_ATTRIBUTED: 403


class AuthorizationError extends ExtendableError 
    static errors = AuthorizationErrors 


export default AuthorizationError 

然后,您可以对错误进行分组,同时使用选项说明符来决定在某些特定于应用程序的情况下采取不同的措施

new AuthorizationError ("The user must be a seller to be able to do a discount", AuthorizationError.errors.BAD_PROFILE_TYPE )

【讨论】:

以上是关于使用 ES6 语法和 Babel 扩展 Javascript 中的错误的主要内容,如果未能解决你的问题,请参考以下文章

使用webpack+babel构建ES6语法运行环境

Babel 无法使用扩展运算符编译 ES6 对象克隆

如何使用Babel将ES6转码为ES5

使用babel转码器,让浏览器支持es6语法

webpack4 使用babel处理ES6语法的一些简单配置

数组扩展符号应该如何与 Babel 中的 NodeLists 一起使用?