[英]Extending Error in Javascript with ES6 syntax & Babel
我正在尝试使用 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 对象永远不会得到正确的消息集。
现在我已经在 SO 上看到了一些解决方案( 例如这里),但它们看起来都非常不符合 ES6 标准。 如何以一种不错的 ES6 方式做到这一点? (这是在 Babel 中工作的)
根据 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 ) 才能工作。
结合这个答案,这个答案和这个代码,我制作了这个小“助手”类,它似乎工作正常。
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);
终于把这件事平息了。 在 Babel 6 中,开发人员明确表示不支持从内置扩展。虽然这个技巧对Map
、 Set
等没有帮助,但它确实适用于Error
。 这很重要,因为可以抛出异常的语言的核心思想之一是允许自定义错误。 这一点很重要,因为 Promise 变得越来越有用,因为它们旨在拒绝 Error 。
可悲的事实是您仍然需要在 ES2015 中以旧方式执行此操作。
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
编辑: Typescript 2.1中的重大更改
扩展 Error、Array 和 Map 等内置函数可能不再有效。
作为建议,您可以在任何 super(...) 调用后立即手动调整原型。
稍微编辑 Lee Benson 的原始答案对我有用。 这还将向实例添加ExtendableError
类的stack
和其他方法。
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 的最新变化,我发现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');
class MyError extends Error {
constructor(message) {
super(message);
this.message = message;
this.name = 'MyError';
}
}
不需要
this.stack = (new Error()).stack;
技巧感谢super()
调用。
虽然上面的代码不能输出堆栈跟踪,除非this.stack = (new Error()).stack;
或Error.captureStackTrace(this, this.constructor.name);
在Babel 中调用。 IMO,这可能是这里的一个问题。
实际上,可以使用此代码片段在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
除了@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
我正在尝试使用 ES6 扩展 Error
那个class MyError extends Error {…}
语法是正确的。
请注意,转译器在继承内置对象方面仍然存在问题。 在你的情况下,
var err = super(m);
Object.assign(this, err);
似乎解决了这个问题。
鉴于此,接受的答案不再有效,您可以始终使用工厂作为替代方案( 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);
我更喜欢比上面描述的更强大的语法。 错误类型的其他方法将帮助您创建漂亮的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}.`;
}
}
正如@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;
}
这对我有用:
/**
* @class AuthorizationError
* @extends {Error}
*/
export class AuthorizationError extends Error {
message = 'UNAUTHORIZED';
name = 'AuthorizationError';
}
不使用 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 ...'
如您所见,堆栈包含错误名称和消息。 我不确定我是否遗漏了什么,但所有其他答案似乎都使事情过于复杂。
我通过这种方式改进了@Lee Benson 的解决方案:
可扩展错误.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 )
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.