Jos*_*son 340 javascript error-handling exception
我想在我的JS代码中抛出一些东西,我希望它们是instanceof Error,但我也想让它们成为别的东西.
在Python中,通常会有一个子类Exception.
在JS中做什么是合适的?
小智 202
唯一的标准字段Error对象是message
属性.(参见MDN或EcmaScript语言规范,第15.11节)其他所有内容都是特定于平台的.
Mosts环境设置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
Run Code Online (Sandbox Code Playgroud)
您可以嗅探堆栈,从中移除不需要的元素并提取fileName和lineNumber等信息,但这样做需要有关当前正在运行的JavaScript平台的信息.大多数情况是不必要的 - 如果你真的想要,你可以在验尸中做到.
Safari是一个值得注意的例外.没有stack
属性,但是抛出的对象的throw
关键字集sourceURL
和line
属性.这些事情保证是正确的.
我在这里可以找到测试用例:JavaScript自制的Error对象比较.
Moh*_*sen 142
在ES6中:
class MyError extends Error {
constructor(message) {
super(message);
this.name = 'MyError';
}
}
Run Code Online (Sandbox Code Playgroud)
Bry*_*eld 43
编辑:请阅读评论.事实证明这只适用于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
}
Run Code Online (Sandbox Code Playgroud)
精简版:
function UserError(message) {
this.constructor.prototype.__proto__ = Error.prototype
Error.captureStackTrace(this, this.constructor)
this.name = this.constructor.name
this.message = message
}
Run Code Online (Sandbox Code Playgroud)
我保留this.constructor.prototype.__proto__ = Error.prototype
在函数内部以保持所有代码在一起.但你也可以替换this.constructor
,UserError
并允许你将代码移到函数外部,所以它只被调用一次.
如果你走那条路,请确保在第一次投掷之前调用该线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)
Run Code Online (Sandbox Code Playgroud)toString
子类时不再存在Error
.所以你还需要添加.
else
this.toString = function () { return this.name + ': ' + this.message }
Run Code Online (Sandbox Code Playgroud)IE不会被认为UserError
是一个IE ,instanceof Error
除非你在你之前运行以下一段时间throw UserError
UserError.prototype = Error.prototype
Run Code Online (Sandbox Code Playgroud)JBE*_*JBE 42
简而言之:
如果您使用的是没有转发器的ES6 :
class CustomError extends Error { /* ... */}
Run Code Online (Sandbox Code Playgroud)如果您使用的是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);
Run Code Online (Sandbox Code Playgroud)
如果您使用的是纯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;
}
Run Code Online (Sandbox Code Playgroud)替代方案:使用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
Run Code Online (Sandbox Code Playgroud)
事实上,从巴贝尔的官方文档,你不能扩展任何内置的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"}
Run Code Online (Sandbox Code Playgroud)
而使用上述方法,不仅可以解决instanceof
问题,还可以保留常规错误console.log
:
console.log(new CustomError('test'));
// output:
// Error: test
// at CustomError (<anonymous>:2:32)
// at <anonymous>:1:5
Run Code Online (Sandbox Code Playgroud)
Rub*_*rgh 28
为了避免出现每种不同类型错误的样板,我将一些解决方案的智慧结合到一个 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;
}
Run Code Online (Sandbox Code Playgroud)
然后,您可以轻松定义新的错误类型,如下所示:
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';
});
Run Code Online (Sandbox Code Playgroud)
Onu*_*rım 26
在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 }
});
}
Run Code Online (Sandbox Code Playgroud)
还要注意,不推荐使用该__proto__
属性,这在其他答案中被广泛使用.
Vic*_*der 19
为了完整起见 - 只是因为之前的答案都没有提到这个方法 - 如果您正在使用Node.js并且不必关心浏览器兼容性,那么内置的内容很容易实现所需的效果inherits
该util
模块(官方文档).
例如,假设您要创建一个自定义错误类,该错误类将错误代码作为第一个参数,将错误消息作为第二个参数:
file 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;
Run Code Online (Sandbox Code Playgroud)
现在你可以实例化并传递/抛出你的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!');
Run Code Online (Sandbox Code Playgroud)
请注意,使用此代码段,堆栈跟踪将具有正确的文件名和行,并且错误实例将具有正确的名称!
这是因为使用了该captureStackTrace
方法,该方法stack
在目标对象上创建了一个属性(在这种情况下,CustomError
被实例化).有关其工作原理的更多详细信息,请查看此处的文档.
Bla*_*ine 18
Crescent Fresh回答高度评价的回答是误导性的.虽然他的警告是无效的,但他还没有解决其他限制.
首先,Crescent的"警告:"段落中的推理没有意义.解释意味着编码"一堆if(错误实例MyError)else ..."与多个catch语句相比在某种程度上是繁琐或冗长的.单个catch块中的多个instanceof语句与多个catch语句一样简洁 - 干净简洁的代码,没有任何技巧.这是模拟Java特有的throwable-subtype特定错误处理的好方法.
WRT"出现子类的message属性未设置",如果使用正确构造的Error子类则不是这种情况.要创建自己的ErrorX Error子类,只需复制以"var MyError ="开头的代码块,将单词"MyError"更改为"ErrorX".(如果要向子类添加自定义方法,请遵循示例文本).
JavaScript错误子类化的真正和重要限制是,对于跟踪和报告堆栈跟踪和实例化位置的JavaScript实现或调试器(如FireFox),您自己的Error子类实现中的位置将被记录为实例化的实例化点.如果您使用了直接错误,那么它将是您运行"new Error(...)"的位置.IE用户可能永远不会注意到,但FF上的Fire Bug用户将看到与这些错误一起报告的无用文件名和行号值,并且必须向下钻取到元素#1的堆栈跟踪以找到真实的实例化位置.
t_d*_*m93 15
除了标准message
属性之外,JavaScript 现在还支持将特定cause
的错误作为可选参数添加到Error
构造函数中:
const error1 = new Error('Error one');
const error2 = new Error('Error two', { cause: error1 });
// error2.cause === error1
Run Code Online (Sandbox Code Playgroud)
JoW*_*Wie 12
这个解决方案怎么样?
而不是使用以下方法抛出自定义错误
throw new MyError("Oops!");
Run Code Online (Sandbox Code Playgroud)
你可以包装Error对象(有点像Decorator):
throw new MyError(Error("Oops!"));
Run Code Online (Sandbox Code Playgroud)
这可以确保所有属性都正确,例如stack,fileName lineNumber等等.
那么你要做的就是复制属性,或者为它们定义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();
};
Run Code Online (Sandbox Code Playgroud)
SeP*_*PeF 12
我不喜欢所有其他答案,太长、太复杂或者没有正确跟踪堆栈。这是我的方法,如果您需要更多自定义道具,请将它们传递给构造函数并将它们设置为名称。
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')
Run Code Online (Sandbox Code Playgroud)
我的解决方案比提供的其他答案更简单,并没有缺点.
它保留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}}
);
Run Code Online (Sandbox Code Playgroud)
用法示例:
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
}
Run Code Online (Sandbox Code Playgroud)
对于需要setPrototypeOf的polyfil的环境:
Object.setPrototypeOf = Object.setPrototypeOf || function (obj, proto) {
obj.__proto__ = proto;
return obj;
};
Run Code Online (Sandbox Code Playgroud)
在上面的例子中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';
Run Code Online (Sandbox Code Playgroud)
正如一些人所说,使用ES6相当容易:
class CustomError extends Error { }
Run Code Online (Sandbox Code Playgroud)
所以我在我的应用程序(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');
}
}
Run Code Online (Sandbox Code Playgroud)
在节点中或直接进入浏览器,它将显示: 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;
}
}
Run Code Online (Sandbox Code Playgroud)
这并不复杂,但我个人发现这是轻松扩展错误的最简单方法。
export default class ExtendableError extends Error {
constructor(message) {
super(message);
this.name = this.constructor.name;
}
}
Run Code Online (Sandbox Code Playgroud)
创建一个类似于所谓的实用程序类ExtendableError
。这个实用程序类的目的是像普通的Error
类一样,但name
默认将属性更改为类的名称,因此很容易扩展错误。
现在,如果你想扩展一个错误,只需要一行。
class MyError extends ExtendableError {}
Run Code Online (Sandbox Code Playgroud)
正如其他人所说,在 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);
}
Run Code Online (Sandbox Code Playgroud)