【问题标题】:Extending Error in Javascript with ES6 syntax & Babel使用 ES6 语法和 Babel 扩展 Javascript 中的错误
【发布时间】: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 中的一个错误,此后已修复。

标签: javascript ecmascript-6 babeljs transpiler


【解决方案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 answerthis answerthis 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);

【讨论】:

【解决方案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);
      

      似乎可以解决问题。

      【讨论】:

      • 真的!但是无论如何都没有设置消息 - 我会写一个新的例子。
      • 我现在重写了例子
      • 显然,“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 )
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2017-12-24
                • 2020-02-24
                • 2016-02-23
                • 1970-01-01
                • 2019-01-28
                相关资源
                最近更新 更多