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

我试图扩展与 ES6和巴别塔的错误。这是不工作。

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的方式?(这是在巴别塔工作)

69154 次浏览

我试图扩展 ES6错误

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

请注意,传输器在继承内置对象方面仍然存在问题,

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

似乎解决了问题。

结合 这个答案这个答案这个密码,我已经创建了这个小型的“助手”类,它似乎工作得很好。

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

根据卡雷尔•比列克(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

它还会将错误消息添加到堆栈跟踪中——这在卡雷尔的示例中没有出现。

如果可用,它还将使用 captureStackTrace

对于 Babel 6,需要 转换-内建-扩展(Npm)才能正常工作。

引用

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

由于 super()呼叫,没有必要使用 this.stack = (new Error()).stack;技巧。

虽然上面的代码不能输出堆栈跟踪,除非在 巴别塔中调用 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

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

终于让这一切结束了。在 Babel6中,开发人员从内置扩展 不支持是明确的。虽然这个技巧 不会有助于像 MapSet等的东西,它确实为 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);

另一方面,Babel6有一个插件允许这样做。

Https://www.npmjs.com/package/babel-plugin-transform-builtin-extend

更新: (截至2016-09-29)经过一些测试后,babel.io 似乎没有正确地解释所有的断言(从自定义扩展的 Error 扩展)。但是在 Ember.jS 扩展中,Error 的工作正如预期的那样: < a href = “ https://member-twiddle.com/d88555a6f408174df0a4c8e0fd6b27ce”rel = “ norefrer”> https://ember-twiddle.com/d88555a6f408174df0a4c8e0fd6b27ce

考虑到这一点,公认的答案不再有效,你总是可以使用工厂作为替代方案(回复) :

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

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

墨尔本2991的回答类似,我确实使用了一个工厂,但是遵循了 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;
}

编辑 : 破坏修改 < a href = “ https://github.com/Microsoft/TypeScript/wiki/Break- Changes #  延伸-內建-like-error-array-and-map-may-no-more-work”rel = “ norefrer”> Type escript 2.1

扩展诸如 Error、 Array 和 Map 之类的内置函数可能不再起作用。

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

编辑李本森原来的答案一点点为我工作。这还将 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);

随着 Babel 6的最新变化,我发现 转换-内建-扩展不再工作了,我最终使用了这种混合的方法:

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

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

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

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

这对我有用:

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

我用这种方法改进了一下“ Lee Benson”的解法:

可扩展错误

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 )