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


132

我正在尝试使用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

错误对象永远不会获得正确的消息集。

在Babel REPL中尝试

现在,我已经看到了一些关于SO的解决方案(例如在这里),但是它们似乎都非常不属于ES6-y。如何以一种不错的ES6方式做到这一点?(在Babel工作)


2
指向Babel REPL的链接似乎表明它现在可以正常工作。我认为这是Babel中的错误,此错误已得到修复。
kybernetikos 16/12/15

Answers:


188

根据KarelBí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-extendnpm)才能正常工作。


1
@MichaelYounkin if (typeof Error.captureStackTrace === 'function') { Error.captureStackTrace(this, this.constructor.name) } else { this.stack = (new Error(message)).stack; } 。我认为最好使用此函数,因为它提供了更多的“本机”调用堆栈并输出错误对象的名称。当然,如果您也仅在服务器端(节点)上使用它,那也不是问题。
李本森

4
@MichaelYounkin我认为这不值得被否决。OP讨论了扩展ES6中的错误。按照这种逻辑,至少一个浏览器几乎缺少所有 ES6。我的解决方案(带有附加的func检查)在最广泛使用的浏览器中提供本机覆盖范围,在每个浏览器中都可以回退,并在Node.js中提供100%的覆盖范围。我同意,如果您始终输入错误的类名,那么this.stack = (new Error(message)).stack您可以...但是,实际上,这并不是什么大问题。
李·本森

6
这在Babel 6中不起作用:new MyError('foo') instanceof MyError === false
Sukima

5
将此代码与babel预编译为NPM模块:extendable-error-class npmjs.com/package/extendable-error-class,可方便避免依赖babel-plugin-transform-b​​uiltin-extend
brillout

3
this.message = message;是多余的super(message);
mathieug '17

39

这个答案这个答案这段代码结合起来,我做了一个小的“ 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);

在REPL中尝试


1
this.stack = (new Error(message)).stack;-否则消息从
Lee Benson

3
我怀疑这不能按需工作,因为如果您这样做:console.log(myerror instanceof ExtendableError); 还在说假的..
MaunoVähä

4
同样的问题,使用instanceof CustomError不起作用,如果您不能使用instanceof,有什么意义呢?
gre

通过message在Error堆栈构造函数中添加,可以进行改进,因此在抛出时在堆栈顶部显示正确的消息:this.stack = (new Error(message)).stack;
Sebastien

1
myerror.name现在返回“错误”。不知道这是否与babel的更高版本有关。请参阅下面的@sukima答案
Eric H.

27

终于把这个休息了。在通天六是明确的,开发商不支持从内置的扩展。虽然这一招不会的东西帮助像MapSet等它确实工作Error。这很重要,因为可以引发异常的语言的核心思想之一就是允许自定义错误。由于Promises旨在拒绝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年9月29日起)经过一些测试,看来babel.io不能正确说明所有的断言(从自定义扩展错误扩展)。但是在Ember.JS中扩展错误按预期方式工作:https ://ember-twiddle.com/d88555a6f408174df0a4c8e0fd6b27ce


是的,通过链接的babel插件,它可以与接受的答案一起正常工作。(但是,结果文件在Node中不起作用,因为它显然没有Reflect)
KarelBílek16年

出于好奇,如果ES2016规范说内置插件是可扩展的,那么为什么像v8和Babel的es5这样的VM会反编译呢?一个类可以像其他原型中的原型链一样扩展类,这不是合理的期望吗?为什么需要在插件中隐藏这样的陶器?
Sukima

当大多数用例只想制作共享行为的简单对象时,这尤其令人沮丧。可以使用的自定义错误Error.toString()。需要进行特殊的调整和旋转来完成此操作,这意味着大多数开发人员会避免使用这种方法,并采取诸如扔字符串而不是错误之类的不良做法。或制作自己的地图一样的对象。为什么需要阻止这种OOP方法?
Sukima

我认为他们并不反对,这只是一些技术问题。不过我不确定!您可以问他们:)项目非常开放
KarelBílek16年

到目前为止,关于该主题的模拟问题的所有答案都留在“ babel不支持”,我认为那是对话的结尾。我的牛肉是缺乏支持,这使得常见的OOP习语变得非常困难,我什至不得不与合作者打架,以使他们脱离样板。我只希望这里是一个干净的替代解决方法。似乎添加插件是最好的选择。
Sukima

15

编辑:打破打字稿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);


10

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

6

报价单

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 console以及Node.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

4

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

4
这不起作用:new MyError('foo') instanceof MyError === false
Sukima

1
确实如此Node.js v7.7.3
Gunar Gessner

2

我正在尝试扩展ES6的错误

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

注意,编译器仍然存在从内置对象继承的问题。就你而言

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

似乎解决了问题。


真正!但是仍然没有设置消息-我将写一个新示例。
卡雷尔·比列克(KarelBílek),2015年

我现在已经重写了示例
KarelBílek2015年


显然,“ super(m)”将返回一个空对象。因此,Object.assign没有帮助。
卡雷尔·比列克(KarelBílek),2015年

@KarelBílek:您正在使用哪种浏览器?Error.call()确实为我返回了一个新的错误实例。
Bergi 2015年

2

鉴于此已被接受的答案不再起作用,您可以始终使用工厂作为替代(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插件,则可接受的答案仍然对我有用。不过也感谢您的回答!
卡雷尔·比列克(KarelBílek),2016年

2

我更喜欢比上述描述更强大的语法。错误类型的其他方法将帮助您创建漂亮的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}.`;
    }
}

1

正如@sukima所提到的,您不能扩展本机JS。OP的问题无法回答。

Melbourne2991的答案类似,我确实使用了工厂,但遵循了MDN针对客户错误类型的建议

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


0

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

如您所见,堆栈同时包含错误名称和消息。我不确定是否遗漏了一些东西,但是所有其他答案似乎使事情变得过于复杂。


0

我这样改进了@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 )
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.