[英]What's a good way to extend Error in JavaScript?
我想在我的 JS 代码中抛出一些东西,我希望它们是 instanceof Error,但我也想让它们成为其他东西。
在 Python 中,通常会继承 Exception。
什么是在 JS 中做的合适的事情?
Error 对象唯一的标准字段是message
属性。 (请参阅MDN或 EcmaScript 语言规范,第 15.11 节)其他一切都是特定于平台的。
大多数环境设置了stack
属性,但是fileName
和lineNumber
在继承中实际上没有用。
因此,简约的方法是:
function MyError(message) {
this.name = 'MyError';
this.message = message;
this.stack = (new Error()).stack;
}
MyError.prototype = new Error; // <-- remove this if you do not
// want MyError to be instanceof Error
您可以嗅探堆栈,从中移除不需要的元素并提取文件名和行号等信息,但这样做需要有关 JavaScript 当前运行平台的信息。 大多数情况下是不必要的——如果你真的想要,你可以在事后分析。
Safari是一个明显的例外。 没有stack
属性,但throw
关键字设置了被抛出对象的sourceURL
和line
属性。 这些东西保证是正确的。
我使用的测试用例可以在这里找到: JavaScript自制的错误对象比较。
在 ES6 中:
class MyError extends Error {
constructor(message) {
super(message);
this.name = 'MyError';
}
}
简而言之:
如果您使用没有转译器的 ES6:
class CustomError extends Error { /* ... */}
如果您使用的是Babel 转译器:
选项 1:使用babel-plugin-transform-builtin-extend
选项 2:自己做(灵感来自同一个库)
function CustomError(...args) {
const instance = Reflect.construct(Error, args);
Reflect.setPrototypeOf(instance, Reflect.getPrototypeOf(this));
return instance;
}
CustomError.prototype = Object.create(Error.prototype, {
constructor: {
value: Error,
enumerable: false,
writable: true,
configurable: true
}
});
Reflect.setPrototypeOf(CustomError, Error);
如果您使用纯 ES5 :
function CustomError(message, fileName, lineNumber) { var instance = new Error(message, fileName, lineNumber); Object.setPrototypeOf(instance, Object.getPrototypeOf(this)); return instance; } CustomError.prototype = Object.create(Error.prototype, { constructor: { value: Error, enumerable: false, writable: true, configurable: true } }); if (Object.setPrototypeOf){ Object.setPrototypeOf(CustomError, Error); } else { CustomError.__proto__ = Error; }
替代方案:使用Classtrophobic框架
解释:
为什么使用 ES6 和 Babel 扩展 Error 类是个问题?
因为不再像这样识别 CustomError 的实例。
class CustomError extends Error {}
console.log(new CustomError('test') instanceof Error);// true
console.log(new CustomError('test') instanceof CustomError);// false
事实上,从 Babel 的官方文档来看,你不能扩展任何内置的 JavaScript 类,例如Date
、 Array
、 DOM
或Error
。
此处描述了该问题:
其他 SO 答案呢?
所有给定的答案都解决了instanceof
问题,但您丢失了常规错误console.log
:
console.log(new CustomError('test'));
// output:
// CustomError {name: "MyError", message: "test", stack: "Error↵ at CustomError (<anonymous>:4:19)↵ at <anonymous>:1:5"}
而使用上述方法,不仅可以解决instanceof
问题,还可以保留常规错误console.log
:
console.log(new CustomError('test'));
// output:
// Error: test
// at CustomError (<anonymous>:2:32)
// at <anonymous>:1:5
编辑:请阅读评论。 事实证明,这只适用于 V8 (Chrome / Node.JS) 我的意图是提供一个跨浏览器的解决方案,它适用于所有浏览器,并在有支持的地方提供堆栈跟踪。
编辑:我制作了这个社区 Wiki 以允许进行更多编辑。
适用于 V8 (Chrome / Node.JS) 的解决方案,适用于 Firefox,并且可以修改为在 IE 中基本正常运行。 (见文末)
function UserError(message) {
this.constructor.prototype.__proto__ = Error.prototype // Make this an instanceof Error.
Error.call(this) // Does not seem necessary. Perhaps remove this line?
Error.captureStackTrace(this, this.constructor) // Creates the this.stack getter
this.name = this.constructor.name; // Used to cause messages like "UserError: message" instead of the default "Error: message"
this.message = message; // Used to set the message
}
精简版:
function UserError(message) {
this.constructor.prototype.__proto__ = Error.prototype
Error.captureStackTrace(this, this.constructor)
this.name = this.constructor.name
this.message = message
}
我将this.constructor.prototype.__proto__ = Error.prototype
保留在函数中以将所有代码保持在一起。 但是你也可以用UserError
替换this.constructor
,这允许你将代码移到函数之外,所以它只被调用一次。
如果你走那条路,请确保在第一次抛出UserError
之前调用该行。
该警告不适用于该函数,因为无论顺序如何,首先创建函数。 因此,您可以将该函数移动到文件末尾,而不会出现问题。
浏览器兼容性
适用于 Firefox 和 Chrome(以及 Node.JS)并实现所有承诺。
Internet Explorer 在以下情况下失败
错误没有err.stack
开始,所以“这不是我的错”。
Error.captureStackTrace(this, this.constructor)
不存在所以你需要做一些其他的事情
if(Error.captureStackTrace) // AKA if not IE Error.captureStackTrace(this, this.constructor)
当您继承Error
时, toString
不再存在。 所以你还需要添加。
else this.toString = function () { return this.name + ': ' + this.message }
IE 不会将UserError
视为一个instanceof Error
除非您在throw UserError
之前运行以下一段时间
UserError.prototype = Error.prototype
为了避免每种不同类型错误的样板文件,我将一些解决方案的智慧结合到一个createErrorType
函数中:
function createErrorType(name, init) {
function E(message) {
if (!Error.captureStackTrace)
this.stack = (new Error()).stack;
else
Error.captureStackTrace(this, this.constructor);
this.message = message;
init && init.apply(this, arguments);
}
E.prototype = new Error();
E.prototype.name = name;
E.prototype.constructor = E;
return E;
}
然后您可以轻松定义新的错误类型,如下所示:
var NameError = createErrorType('NameError', function (name, invalidChar) {
this.message = 'The name ' + name + ' may not contain ' + invalidChar;
});
var UnboundError = createErrorType('UnboundError', function (variableName) {
this.message = 'Variable ' + variableName + ' is not bound';
});
2018年,我认为这是最好的方式; 支持 IE9+ 和现代浏览器。
function CustomError(message) {
Object.defineProperty(this, 'name', {
enumerable: false,
writable: false,
value: 'CustomError'
});
Object.defineProperty(this, 'message', {
enumerable: false,
writable: true,
value: message
});
if (Error.hasOwnProperty('captureStackTrace')) { // V8
Error.captureStackTrace(this, CustomError);
} else {
Object.defineProperty(this, 'stack', {
enumerable: false,
writable: false,
value: (new Error(message)).stack
});
}
}
if (typeof Object.setPrototypeOf === 'function') {
Object.setPrototypeOf(CustomError.prototype, Error.prototype);
} else {
CustomError.prototype = Object.create(Error.prototype, {
constructor: { value: CustomError }
});
}
还要注意__proto__
属性已被弃用,这在其他答案中被广泛使用。
Crescent Fresh 的回答高票回答具有误导性。 尽管他的警告无效,但还有其他限制他没有解决。
首先,Crescent 的“警告:”段落中的推理没有意义。 该解释暗示,与多个 catch 语句相比,编码“一堆 if (error instanceof MyError) else ...”在某种程度上是繁琐或冗长的。 单个 catch 块中的多个 instanceof 语句与多个 catch 语句一样简洁——干净简洁的代码没有任何技巧。 这是模拟 Java 出色的 throwable-subtype-specific 错误处理的好方法。
WRT“似乎没有设置子类的消息属性”,如果您使用正确构造的错误子类,则情况并非如此。 要创建您自己的 ErrorX Error 子类,只需复制以“var MyError =”开头的代码块,将单词“MyError”更改为“ErrorX”。 (如果要向子类添加自定义方法,请按照示例文本进行操作)。
JavaScript 错误子类化的真正且重要的限制是,对于跟踪和报告堆栈跟踪和实例化位置的 JavaScript 实现或调试器,如 FireFox,您自己的 Error 子类实现中的一个位置将被记录为实例化点类,而如果您使用直接错误,它将是您运行“new Error(...)”的位置)。 IE 用户可能永远不会注意到,但 FF 上的 Fire Bug 用户将看到与这些错误一起报告的无用文件名和行号值,并且必须深入到元素 #1 的堆栈跟踪以找到真正的实例化位置。
为了完整起见——仅仅因为之前的答案都没有提到这个方法——如果你正在使用 Node.js 并且不必关心浏览器兼容性,那么使用内置的util
模块的inherits
( 此处为官方文档)。
例如,假设您要创建一个自定义错误类,该类将错误代码作为第一个参数,将错误消息作为第二个参数:
文件custom-error.js :
'use strict';
var util = require('util');
function CustomError(code, message) {
Error.captureStackTrace(this, CustomError);
this.name = CustomError.name;
this.code = code;
this.message = message;
}
util.inherits(CustomError, Error);
module.exports = CustomError;
现在您可以实例化并传递/抛出您的CustomError
:
var CustomError = require('./path/to/custom-error');
// pass as the first argument to your callback
callback(new CustomError(404, 'Not found!'));
// or, if you are working with try/catch, throw it
throw new CustomError(500, 'Server Error!');
请注意,使用此代码段,堆栈跟踪将具有正确的文件名和行,并且错误实例将具有正确的名称!
这是由于使用了captureStackTrace
方法,该方法在目标对象上创建了一个stack
属性(在本例中, CustomError
被实例化)。 有关其工作原理的更多详细信息,请查看此处的文档。
正如一些人所说,使用 ES6 相当容易:
class CustomError extends Error { }
所以我在我的应用程序(Angular,Typescript)中尝试了它,但它不起作用。 一段时间后,我发现问题来自 Typescript :O
见https://github.com/Microsoft/TypeScript/issues/13965
这非常令人不安,因为如果你这样做:
class CustomError extends Error {}
try {
throw new CustomError()
} catch(e) {
if (e instanceof CustomError) {
console.log('Custom error');
} else {
console.log('Basic error');
}
}
在节点或直接进入您的浏览器,它将显示: Custom error
尝试在 Typescript 操场上的项目中使用 Typescript 运行它,它会显示Basic error
...
解决方案是执行以下操作:
class CustomError extends Error {
// we have to do the following because of: https://github.com/Microsoft/TypeScript/issues/13965
// otherwise we cannot use instanceof later to catch a given type
public __proto__: Error;
constructor(message?: string) {
const trueProto = new.target.prototype;
super(message);
this.__proto__ = trueProto;
}
}
这个解决方案怎么样?
而不是使用以下方法抛出您的自定义错误:
throw new MyError("Oops!");
您将包装 Error 对象(有点像装饰器):
throw new MyError(Error("Oops!"));
这确保所有属性都是正确的,例如堆栈、文件名、行号等。
然后你所要做的就是复制这些属性,或者为它们定义 getter。 这是一个使用 getter (IE9) 的示例:
function MyError(wrapped)
{
this.wrapped = wrapped;
this.wrapped.name = 'MyError';
}
function wrap(attr)
{
Object.defineProperty(MyError.prototype, attr, {
get: function()
{
return this.wrapped[attr];
}
});
}
MyError.prototype = Object.create(Error.prototype);
MyError.prototype.constructor = MyError;
wrap('name');
wrap('message');
wrap('stack');
wrap('fileName');
wrap('lineNumber');
wrap('columnNumber');
MyError.prototype.toString = function()
{
return this.wrapped.toString();
};
我的解决方案比提供的其他答案更简单,并且没有缺点。
它保留了 Error 原型链和 Error 上的所有属性,而无需具体了解它们。 它已经在 Chrome、Firefox、Node 和 IE11 中进行了测试。
唯一的限制是调用堆栈顶部的额外条目。 但这很容易被忽视。
这是一个带有两个自定义参数的示例:
function CustomError(message, param1, param2) {
var err = new Error(message);
Object.setPrototypeOf(err, CustomError.prototype);
err.param1 = param1;
err.param2 = param2;
return err;
}
CustomError.prototype = Object.create(
Error.prototype,
{name: {value: 'CustomError', enumerable: false}}
);
示例用法:
try {
throw new CustomError('Something Unexpected Happened!', 1234, 'neat');
} catch (ex) {
console.log(ex.name); //CustomError
console.log(ex.message); //Something Unexpected Happened!
console.log(ex.param1); //1234
console.log(ex.param2); //neat
console.log(ex.stack); //stacktrace
console.log(ex instanceof Error); //true
console.log(ex instanceof CustomError); //true
}
对于需要 setPrototypeOf 的 polyfil 的环境:
Object.setPrototypeOf = Object.setPrototypeOf || function (obj, proto) {
obj.__proto__ = proto;
return obj;
};
在上面的例子中Error.apply
(也是Error.call
)对我没有任何作用(Firefox 3.6/Chrome 5)。 我使用的解决方法是:
function MyError(message, fileName, lineNumber) {
var err = new Error();
if (err.stack) {
// remove one stack level:
if (typeof(Components) != 'undefined') {
// Mozilla:
this.stack = err.stack.substring(err.stack.indexOf('\n')+1);
}
else if (typeof(chrome) != 'undefined' || typeof(process) != 'undefined') {
// Google Chrome/Node.js:
this.stack = err.stack.replace(/\n[^\n]*/,'');
}
else {
this.stack = err.stack;
}
}
this.message = message === undefined ? err.message : message;
this.fileName = fileName === undefined ? err.fileName : fileName;
this.lineNumber = lineNumber === undefined ? err.lineNumber : lineNumber;
}
MyError.prototype = new Error();
MyError.prototype.constructor = MyError;
MyError.prototype.name = 'MyError';
除了标准message
属性之外,JavaScript 现在支持将错误的特定cause
作为可选参数添加到Error
构造函数中:
const error1 = new Error('Error one');
const error2 = new Error('Error two', { cause: error1 });
// error2.cause === error1
正如其他人所说,在 Node 中,这很简单:
class DumbError extends Error {
constructor(foo = 'bar', ...params) {
super(...params);
if (Error.captureStackTrace) {
Error.captureStackTrace(this, DumbError);
}
this.name = 'DumbError';
this.foo = foo;
this.date = new Date();
}
}
try {
let x = 3;
if (x < 10) {
throw new DumbError();
}
} catch (error) {
console.log(error);
}
我只想补充其他人已经说过的内容:
为了确保自定义错误类在堆栈跟踪中正确显示,您需要将自定义错误类的原型名称属性设置为自定义错误类的名称属性。 这就是我的意思:
CustomError.prototype = Error.prototype;
CustomError.prototype.name = 'CustomError';
所以完整的例子是:
var CustomError = function(message) {
var err = new Error(message);
err.name = 'CustomError';
this.name = err.name;
this.message = err.message;
//check if there is a stack property supported in browser
if (err.stack) {
this.stack = err.stack;
}
//we should define how our toString function works as this will be used internally
//by the browser's stack trace generation function
this.toString = function() {
return this.name + ': ' + this.message;
};
};
CustomError.prototype = new Error();
CustomError.prototype.name = 'CustomError';
当一切都完成后,你抛出你的新异常,它看起来像这样(我懒洋洋地在 chrome 开发工具中尝试了这个):
CustomError: Stuff Happened. GASP!
at Error.CustomError (<anonymous>:3:19)
at <anonymous>:2:7
at Object.InjectedScript._evaluateOn (<anonymous>:603:39)
at Object.InjectedScript._evaluateAndWrap (<anonymous>:562:52)
at Object.InjectedScript.evaluate (<anonymous>:481:21)
我的 2 美分:
a) 因为访问Error.stack
属性(如在某些答案中)会造成很大的性能损失。
b) 因为它只有一行。
c) 因为https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error上的解决方案似乎没有保留堆栈信息。
//MyError class constructor
function MyError(msg){
this.__proto__.__proto__ = Error.apply(null, arguments);
};
使用示例
http://jsfiddle.net/luciotato/xXyeB/
this.__proto__.__proto__
是MyError.prototype.__proto__
,因此它将MyError.prototype.__proto__
的__proto__
FOR ALL INSTANCES 设置为特定的新创建的错误。 它保留 MyError 类属性和方法,并将新的 Error 属性(包括 .stack)放入__proto__
链中。
您不能拥有多个具有有用堆栈信息的 MyError 实例。
如果您不完全了解this.__proto__.__proto__=
作用,请不要使用此解决方案。
由于 JavaScript 异常很难进行子类化,因此我不进行子类化。 我只是创建了一个新的 Exception 类并在其中使用了一个 Error 。 我更改了 Error.name 属性,使其看起来像我在控制台上的自定义异常:
var InvalidInputError = function(message) {
var error = new Error(message);
error.name = 'InvalidInputError';
return error;
};
上面的新异常可以像常规错误一样抛出,它会按预期工作,例如:
throw new InvalidInputError("Input must be a string");
// Output: Uncaught InvalidInputError: Input must be a string
警告:堆栈跟踪并不完美,因为它会将您带到创建新错误的位置,而不是您抛出的位置。 这在 Chrome 上没什么大不了的,因为它直接在控制台中为您提供了完整的堆栈跟踪。 但是,例如,在 Firefox 上问题更多。
正如 Mohsen 的回答中所指出的,在 ES6 中,可以使用类来扩展错误。 这要容易得多,而且它们的行为更符合本机错误……但不幸的是,如果您需要支持 ES6 之前的浏览器,在浏览器中使用它并不是一件简单的事情。 有关如何实施的一些说明,请参见下文,但与此同时,我建议采用一种相对简单的方法,该方法结合了其他答案中的一些最佳建议:
function CustomError(message) {
//This is for future compatibility with the ES6 version, which
//would display a similar message if invoked without the
//`new` operator.
if (!(this instanceof CustomError)) {
throw new TypeError("Constructor 'CustomError' cannot be invoked without 'new'");
}
this.message = message;
//Stack trace in V8
if (Error.captureStackTrace) {
Error.captureStackTrace(this, CustomError);
}
else this.stack = (new Error).stack;
}
CustomError.prototype = Object.create(Error.prototype);
CustomError.prototype.name = 'CustomError';
在 ES6 中,它很简单:
class CustomError extends Error {}
...并且您可以使用try {eval('class X{}')
检测对 ES6 类的支持,但是如果您尝试在旧浏览器加载的脚本中包含 ES6 版本,则会出现语法错误。 因此,支持所有浏览器的唯一方法是为支持 ES6 的浏览器动态加载单独的脚本(例如,通过 AJAX 或eval()
)。 更复杂的是,并非所有环境都支持eval()
(由于内容安全策略),这可能是您的项目的考虑因素,也可能不是。
所以就目前而言,无论是上面的第一种方法还是直接使用Error
而不尝试扩展它似乎是对于需要支持非 ES6 浏览器的代码实际上可以做到的最好的方法。
有些人可能想要考虑另一种方法,即使用Object.setPrototypeOf()
来创建一个错误对象,该对象是自定义错误类型的实例,但其外观和行为更像是控制台中的本机错误(感谢Ben对推荐的回答)。 这是我对这种方法的看法: https : //gist.github.com/mbrowne/fe45db61cea7858d11be933a998926a8 。 但考虑到有一天我们将能够只使用 ES6,我个人不确定这种方法的复杂性是否值得。
Mohsen 在 ES6 上面有一个很好的答案来设置名称,但是如果你正在使用 TypeScript 或者如果你生活在未来,希望这个关于公共和私有类字段的提案已经作为提案通过了第 3 阶段并成功了作为 ECMAScript/JavaScript 的一部分进入第 4 阶段,那么您可能想知道这会更短一些。 第 3 阶段是浏览器开始实现功能的地方,因此如果您的浏览器支持它,下面的代码可能会起作用。 (在新的 Edge 浏览器 v81 中测试它似乎工作正常)。 请注意,尽管目前这是一个不稳定的功能,应该谨慎使用,并且您应该始终检查浏览器对不稳定功能的支持。 这篇文章主要针对那些浏览器可能支持此功能的未来居民。 要检查支持检查MDN和我可以使用. 它目前在浏览器市场上获得了 66% 的支持,但不是那么好,所以如果你现在真的想使用它并且不想等待,要么使用像Babel这样的转译器或像TypeScript这样的东西。
class EOFError extends Error { name="EOFError" } throw new EOFError("Oops errored");
将此与无名错误进行比较,该错误在抛出时不会记录其名称。
class NamelessEOFError extends Error {} throw new NamelessEOFError("Oops errored");
正确执行此操作的方法是从构造函数返回 apply 的结果,并以通常复杂的 javascripty 方式设置原型:
function MyError() {
var tmp = Error.apply(this, arguments);
tmp.name = this.name = 'MyError'
this.stack = tmp.stack
this.message = tmp.message
return this
}
var IntermediateInheritor = function() {}
IntermediateInheritor.prototype = Error.prototype;
MyError.prototype = new IntermediateInheritor()
var myError = new MyError("message");
console.log("The message is: '"+myError.message+"'") // The message is: 'message'
console.log(myError instanceof Error) // true
console.log(myError instanceof MyError) // true
console.log(myError.toString()) // MyError: message
console.log(myError.stack) // MyError: message \n
// <stack trace ...>
在这一点上这样做的唯一问题(我已经迭代了一点)是
stack
和message
之外的属性不包含在MyError
和第一个问题可以通过使用此答案中的技巧迭代所有不可枚举的错误属性来解决: Is it possible to get the non-enumerable继承的属性名称对象? ,但这不受 ie<9 支持。 第二个问题可以通过删除堆栈跟踪中的那一行来解决,但我不确定如何安全地做到这一点(也许只是删除 e.stack.toString() 的第二行??)。
片段显示了这一切。
function add(x, y) {
if (x && y) {
return x + y;
} else {
/**
*
* the error thrown will be instanceof Error class and InvalidArgsError also
*/
throw new InvalidArgsError();
// throw new Invalid_Args_Error();
}
}
// Declare custom error using using Class
class Invalid_Args_Error extends Error {
constructor() {
super("Invalid arguments");
Error.captureStackTrace(this);
}
}
// Declare custom error using Function
function InvalidArgsError(message) {
this.message = `Invalid arguments`;
Error.captureStackTrace(this);
}
// does the same magic as extends keyword
Object.setPrototypeOf(InvalidArgsError.prototype, Error.prototype);
try{
add(2)
}catch(e){
// true
if(e instanceof Error){
console.log(e)
}
// true
if(e instanceof InvalidArgsError){
console.log(e)
}
}
我不喜欢所有其他答案,太长、太复杂或没有正确跟踪堆栈。 这是我的方法,如果您需要更多自定义道具,请将它们传递给构造函数并将它们设置为名称。
class CustomError extends Error {
constructor (message) {
super(message)
// needed for CustomError instanceof Error => true
Object.setPrototypeOf(this, new.target.prototype);
// Set the name
this.name = this.constructor.name
// Maintains proper stack trace for where our error was thrown (only available on V8)
if (Error.captureStackTrace) {
Error.captureStackTrace(this, this.constructor)
}
}
}
// create own CustomError sub classes
class SubCustomError extends CustomError{}
// Tests
console.log(new SubCustomError instanceof CustomError) // true
console.log(new SubCustomError instanceof CustomError) // true
console.log(new CustomError instanceof Error) // true
console.log(new SubCustomError instanceof Error) // true
throw new SubCustomError ('test error')
我提出的解决方案是使用 error 的.name
属性来区分错误类型而不是instancof
这并不能完全回答这个问题,但我认为无论如何对于某些情况,这是一个合理的解决方案。
我已经看到能够拥有instanceof CustomError
的好处是您可以在您的承诺捕获处理程序中进行自定义处理。
例如:
class CustomError extends Error {/** ... **/}
axios
.post(url, payload)
.then(data => {
if (!data.loggedIn) throw CustomError("not logged in");
return data;
})
.catch(error => {
if (error instanceof CustomError) {/** custom handling of error*//}
throw error
})
如果这就是您要完成的任务,那么您也很适合.name
参数:
export const ERROR_NOT_LOGGED_IN = "ERROR_NOT_LOGGED_IN";
axios
.post(url, payload)
.then(data => {
if (!data.loggedIn) throw Error("not logged in").name=ERROR_NOT_LOGGED_IN ;
return data;
})
.catch(error => {
if (error.name === ERROR_NOT_LOGGED_IN) {/** custom handling of error*//}
throw error
})
我会退后一步考虑你为什么要这样做? 我认为关键是要以不同的方式处理不同的错误。
例如,在 Python 中,您可以将 catch 语句限制为仅捕获MyValidationError
,也许您希望能够在 javascript 中执行类似的操作。
catch (MyValidationError e) {
....
}
你不能在 javascript 中做到这一点。 只会有一个 catch 块。 您应该对错误使用 if 语句来确定其类型。
catch(e) { if(isMyValidationError(e)) { ... } else { // maybe rethrow? throw e; } }
我想我会抛出一个带有类型、消息和任何其他你认为合适的属性的原始对象。
throw { type: "validation", message: "Invalid timestamp" }
当您发现错误时:
catch(e) {
if(e.type === "validation") {
// handle error
}
// re-throw, or whatever else
}
这是基于George Bailey 的回答,但扩展并简化了最初的想法。 它是用 CoffeeScript 编写的,但很容易转换为 JavaScript。 这个想法是使用包装它的装饰器扩展 Bailey 的自定义错误,让您轻松创建新的自定义错误。
注意:这仅适用于 V8。 在其他环境中不支持Error.captureStackTrace
。
装饰器为错误类型取一个名称,并返回一个接收错误消息并包含错误名称的函数。
CoreError = (@message) ->
@constructor.prototype.__proto__ = Error.prototype
Error.captureStackTrace @, @constructor
@name = @constructor.name
BaseError = (type) ->
(message) -> new CoreError "#{ type }Error: #{ message }"
现在很容易创建新的错误类型。
StorageError = BaseError "Storage"
SignatureError = BaseError "Signature"
为了好玩,你现在可以定义一个函数,如果它被调用的参数太多,就会抛出一个SignatureError
。
f = -> throw SignatureError "too many args" if arguments.length
这已经过很好的测试,似乎在 V8 上工作得很好,维护了回溯、位置等。
注意:在构建自定义错误时使用new
是可选的。
如果你不关心错误的表现,这是你能做的最小的事情
Object.setPrototypeOf(MyError.prototype, Error.prototype)
function MyError(message) {
const error = new Error(message)
Object.setPrototypeOf(error, MyError.prototype);
return error
}
您可以在没有新的 MyError(message) 的情况下使用它
通过在构造函数 Error 被调用后更改原型,我们不必设置调用堆栈和消息
这并不复杂,但我个人认为这是轻松扩展错误的最简单方法。
export default class ExtendableError extends Error {
constructor(message) {
super(message);
this.name = this.constructor.name;
}
}
创建一个实用程序类,如所谓的ExtendableError
。 这个实用类的目的是和普通的Error
类一样,但是默认把name
属性改成类的name
,这样扩展错误真的很容易。
现在,如果你想扩展一个错误,它只需要一行。
class MyError extends ExtendableError {}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.