首页 文章

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

提问于
浏览
117

我试图用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-y . 如何以漂亮的ES6方式做到这一点? (那是在Babel工作)

11 回答

  • 1

    基于KarelBílek's answer, I' d对 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-extendnpm)才能工作 .

  • 37

    最后让这个休息 . 在Babel 6中明确表示开发人员不支持从内置扩展 . 尽管这个技巧对 MapSet 等内容没有帮助,但它确实适用于 Error . 这很重要,因为可以引发异常的语言的核心思想之一是允许自定义错误 . 这是非常重要的,因为Promises变得更有用,因为它们被设计为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

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

  • 2

    随着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');
    
  • 8

    鉴于此接受的答案不再有效,您可以随时使用工厂作为替代方案(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);
    
  • 0

    除了@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
    
  • 2

    正如@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;
    }
    
  • 159

    Quoting

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

    不需要this.stack =(new Error()) . stack;感谢super()调用 .

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

    实际上,可以使用此代码片段在 Chrome consoleNode.js v4.2.1 下输出堆栈跟踪 .

    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
    
  • 24

    结合this answerthis answerthis code,我已经制作了这个小的"helper"类,看起来效果很好 .

    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

  • 4

    EditTypescript 2.1中的重大变化

    扩展错误,数组和 Map 等内置函数可能不再有效 . 作为建议,您可以在任何超级(...)调用后立即手动调整原型 .

    编辑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);
    
  • 4

    我试图用ES6扩展Error

    class MyError extends Error {…} 语法是正确的 .

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

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

    似乎解决了这个问题 .

  • 10

    不使用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 ...'
    

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

相关问题