I want to throw some things in my JS code and I want them to be instanceof Error, but I also want to have them be something else.
我想在我的JS代码中加入一些东西,我希望它们是instanceof Error,但我也希望它们是其他的东西。
In Python, typically, one would subclass Exception.
在Python中,通常会有一个子类异常。
What's the appropriate thing to do in JS?
用JS做什么比较合适?
19 个解决方案
#1
175
The only standard field Error object has is the message
property. (See MDN, or EcmaScript Language Specification, section 15.11) Everything else is platform specific.
唯一的标准字段错误对象是message属性。(参见MDN或EcmaScript语言规范第15.11节)其他内容都是特定于平台的。
Mosts environments set the stack
property, but fileName
and lineNumber
are practically useless to be used in inheritance.
Mosts环境设置堆栈属性,但是文件名和lineNumber在继承中实际上是无用的。
So, the minimalistic approach is:
所以,极简主义的方法是:
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
You could sniff the stack, unshift unwanted elements from it and extract information like fileName and lineNumber, but doing so requires information about the platform JavaScript is currently running upon. Most cases that is unnecessary -- and you can do it in post-mortem if you really want.
您可以嗅探堆栈,从堆栈中移出不需要的元素,并提取文件名和lineNumber等信息,但是这样做需要关于当前运行的平台JavaScript的信息。大多数情况下这是不必要的——如果你真的想做,你可以在尸检后再做。
Safari is a notable exception. There is no stack
property, but the throw
keyword sets sourceURL
and line
properties of the object that is being thrown. Those things are guaranteed to be correct.
Safari是个明显的例外。没有堆栈属性,但是throw关键字设置正在抛出的对象的sourceURL和行属性。这些东西肯定是正确的。
Test cases I used can be found here: JavaScript self-made Error object comparison.
我使用的测试用例可以在这里找到:JavaScript自制错误对象比较。
#2
109
In ES6:
在ES6:
class MyError extends Error {
constructor(message) {
super(message);
this.name = 'MyError';
}
}
源
#3
38
Edit: Please read comments. It turns out this only works well in V8 (Chrome / Node.JS) My intent was to provide a cross-browser solution, which would work in all browsers, and provide stack trace where support is there.
编辑:请阅读评论。我的目的是提供一个跨浏览器的解决方案,它可以在所有浏览器中工作,并在支持的地方提供堆栈跟踪。
Edit: I made this Community Wiki to allow for more editing.
编辑:我创建这个社区Wiki是为了允许更多的编辑。
Solution for V8 (Chrome / Node.JS), works in Firefox, and can be modified to function mostly correctly in IE. (see end of post)
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
}
Original post on "Show me the code !"
原来的帖子“给我看代码!”
Short version:
短版:
function UserError(message) {
this.constructor.prototype.__proto__ = Error.prototype
Error.captureStackTrace(this, this.constructor)
this.name = this.constructor.name
this.message = message
}
I keep this.constructor.prototype.__proto__ = Error.prototype
inside the function to keep all the code together. But you can also replace this.constructor
with UserError
and that allows you to move the code to outside the function, so it only gets called once.
我一直this.constructor.prototype。__proto__ =错误。在函数内部的原型,以保持所有的代码在一起。但是你也可以替换它。带有UserError的构造函数,允许您将代码移到函数之外,因此只调用一次。
If you go that route, make sure you call that line before the first time you throw UserError
.
如果您走那条路线,请确保在第一次抛出UserError之前调用该线。
That caveat does not apply the function, because functions are created first, no matter the order. Thus, you can move the function to the end of the file, without a problem.
这个警告不应用函数,因为函数是先创建的,不管顺序如何。因此,可以毫无问题地将函数移动到文件的末尾。
Browser Compatibility
浏览器兼容性
Works in Firefox and Chrome (and Node.JS) and fills all promises.
适用于Firefox和Chrome(以及Node.JS),并满足所有的承诺。
Internet Explorer fails in the following
Internet Explorer在以下方面失败
-
Errors do not have
err.stack
to begin with, so "it's not my fault".错误没有错。堆栈开始,所以“这不是我的错”。
-
Error.captureStackTrace(this, this.constructor)
does not exist so you need to do something else like错误。captureStackTrace(this, this.构造函数)不存在,所以需要做一些类似的事情
if(Error.captureStackTrace) // AKA if not IE Error.captureStackTrace(this, this.constructor)
-
toString
ceases to exist when you subclassError
. So you also need to add.当您子类化错误时,toString就不存在了。所以你也需要添加。
else this.toString = function () { return this.name + ': ' + this.message }
-
IE will not consider
UserError
to be aninstanceof Error
unless you run the following some time before youthrow UserError
IE不会认为UserError是一个instanceof错误,除非您在抛出UserError之前运行以下代码
UserError.prototype = Error.prototype
#4
24
To avoid the boilerplate for every different type of error, I combined the wisdom of some of the solutions into a createErrorType
function:
为了避免出现每种不同类型的错误,我将一些解决方案的智慧结合到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;
}
Then you can define new error types easily as follows:
然后您可以轻松定义新的错误类型如下:
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';
});
#5
18
Crescent Fresh's answer highly-voted answer is misleading. Though his warnings are invalid, there are other limitations that he doesn't address.
新月形新鲜的回答高投票的回答是误导。尽管他的警告是无效的,但还有其他一些限制他没有提及。
First, the reasoning in Crescent's "Caveats:" paragraph doesn't make sense. The explanation implies that coding "a bunch of if (error instanceof MyError) else ..." is somehow burdensome or verbose compared to multiple catch statements. Multiple instanceof statements in a single catch block are just as concise as multiple catch statements-- clean and concise code without any tricks. This is a great way to emulate Java's great throwable-subtype-specific error handling.
首先,新月的“警告”中的推理是没有意义的。这种解释意味着,与多个catch语句相比,编写“一堆if (error instanceof MyError) else……”可能有些麻烦或冗长。一个catch块中的多个instanceof语句与多个catch语句一样简洁——干净简洁的代码没有任何技巧。这是一种很好的方法来模拟Java出色的、特定于抛出子类型的错误处理。
WRT "appears the message property of the subclass does not get set", that is not the case if you use a properly constructed Error subclass. To make your own ErrorX Error subclass, just copy the code block beginning with "var MyError =", changing the one word "MyError" to "ErrorX". (If you want to add custom methods to your subclass, follow the sample text).
WRT“出现子类的消息属性未被设置”,如果您使用一个正确构造的错误子类,则不是这种情况。要创建自己的ErrorX错误子类,只需复制以“var MyError =”开头的代码块,将一个单词“MyError”更改为“ErrorX”。(如果希望向子类添加自定义方法,请遵循示例文本)。
The real and significant limitation of JavaScript error subclassing is that for JavaScript implementations or debuggers that track and report on stack trace and location-of-instantiation, like FireFox, a location in your own Error subclass implementation will be recorded as the instantiation point of the class, whereas if you used a direct Error, it would be the location where you ran "new Error(...)"). IE users would probably never notice, but users of Fire Bug on FF will see useless file name and line number values reported alongside these Errors, and will have to drill down on the stack trace to element #1 to find the real instantiation location.
JavaScript错误子类化的真实和重要的限制是JavaScript实现或调试器,跟踪和报告在堆栈跟踪和location-of-instantiation,像FireFox,位置在你自己的错误子类实现将记录为实例化的类,而如果使用直接错误,它将是你的位置跑“新的错误(…)”)。IE用户可能永远不会注意到,但是FF上的Fire Bug用户将会看到无用的文件名和行号值与这些错误一起报告,并且必须深入到堆栈跟踪到元素#1以找到真正的实例化位置。
#6
17
In 2017, I think this is the best way; that supports IE9+ and modern browsers.
在2017年,我认为这是最好的方式;它支持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 }
});
}
Also beware that __proto__
property is deprecated which is widely used in other answers.
还要注意__proto__属性已被弃用,这在其他答案中被广泛使用。
Note: See discussion in the comments and @MattBrowne's gist.
注:请参阅评论和@MattBrowne的要点讨论。
UPDATE: Added constructor
assignment. See this test for comparison on different implementations. Test module here.
更新:添加构造函数赋值。有关不同实现的比较,请参见此测试。测试模块。
#7
16
In short:
简而言之:
-
If you are using ES6 without transpilers:
如果你使用的是ES6,没有运输工具:
class CustomError extends Error { /* ... */}
-
If you are using Babel transpiler:
如果你使用的是Babel transiler:
Option 1: use babel-plugin-transform-builtin-extend
选项1:使用babel-plugin-transform-builtin-extend
Option 2: do it yourself (inspired from that same library)
选项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);
-
If you are using pure ES5:
如果你使用纯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; }
-
Alternative: use Classtrophobic framework
选择:使用Classtrophobic框架
Explanation:
解释:
Why extending the Error class using ES6 and Babel is a problem?
为什么使用ES6和Babel扩展错误类是一个问题?
Because an instance of CustomError is not anymore recognized as such.
因为CustomError的实例不再是这样识别的。
class CustomError extends Error {}
console.log(new CustomError('test') instanceof Error);// true
console.log(new CustomError('test') instanceof CustomError);// false
In fact, from the official documentation of Babel, you cannot extend any built-in JavaScript classes such as Date
, Array
, DOM
or Error
.
事实上,从Babel的官方文档中,您不能扩展任何内置的JavaScript类,比如Date、Array、DOM或Error。
The issue is described here:
问题如下:
- Native extends breaks HTMLELement, Array, and others
- 本机扩展中断HTMLELement、Array和其他
- an object of The class which is extends by base type like Array,Number,Object,String or Error is not instanceof this class
- 由基类型扩展的类的对象(如数组、数字、对象、字符串或错误)不是该类的实例
What about the other SO answers?
那么另一个呢?
All the given answers fix the instanceof
issue but you lose the regular error console.log
:
所有给定的答案都修复了实例问题,但是您失去了常规的错误控制。
console.log(new CustomError('test'));
// output:
// CustomError {name: "MyError", message: "test", stack: "Error↵ at CustomError (<anonymous>:4:19)↵ at <anonymous>:1:5"}
Whereas using the method mentioned above, not only you fix the instanceof
issue but you also keep the regular error console.log
:
使用上面提到的方法,不仅可以修复instanceof问题,还可以保持常规的error console.log:
console.log(new CustomError('test'));
// output:
// Error: test
// at CustomError (<anonymous>:2:32)
// at <anonymous>:1:5
#8
14
For the sake of completeness -- just because none of the previous answers mentioned this method -- if you are working with Node.js and don't have to care about browser compatibility, the desired effect is pretty easy to achieve with the built in inherits
of the util
module (official docs here).
为了完整性起见——仅仅因为前面的答案中没有提到这个方法——如果您使用的是Node。不需要关心浏览器的兼容性,通过继承util模块(这里的官方文档)来实现所需的效果非常容易。
For example, let's suppose you want to create a custom error class that takes an error code as the first argument and the error message as the second argument:
例如,假设您想创建一个自定义错误类,它将错误代码作为第一个参数,错误消息作为第二个参数:
file custom-error.js:
文件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;
Now you can instantiate and pass/throw your 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!');
Note that, with this snippet, the stack trace will have the correct file name and line, and the error instance will have the correct name!
注意,使用此代码段,堆栈跟踪将具有正确的文件名和行,错误实例将具有正确的名称!
This happens due to the usage of the captureStackTrace
method, which creates a stack
property on the target object (in this case, the CustomError
being instantiated). For more details about how it works, check the documentation here.
这是由于使用captureStackTrace方法导致的,该方法在目标对象上创建堆栈属性(在本例中,正在实例化CustomError)。有关它如何工作的更多细节,请查看这里的文档。
#9
11
How about this solution?
该解决方案如何?
Instead of throwing your custom Error using:
不要使用:
throw new MyError("Oops!");
You would wrap the Error object (kind of like a Decorator):
您将封装错误对象(类似于Decorator):
throw new MyError(Error("Oops!"));
This makes sure all of the attributes are correct, such as the stack, fileName lineNumber, et cetera.
这确保所有属性都是正确的,例如堆栈、文件名lineNumber等等。
All you have to do then is either copy over the attributes, or define getters for them. Here is an example using getters (IE9):
然后,您所要做的就是复制这些属性,或者为它们定义getter。这里有一个使用getters (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();
};
#10
9
My solution is more simple than the other answers provided and doesn't have the downsides.
我的解决方案比提供的其他答案更简单,没有缺点。
It preserves the Error prototype chain and all properties on Error without needing specific knowledge of them. It's been tested in Chrome, Firefox, Node, and IE11.
它保留了错误原型链和所有的错误属性,而不需要特定的知识。它已经在Chrome、Firefox、Node和IE11中进行了测试。
The only limitation is an extra entry at the top of the call stack. But that is easily ignored.
唯一的限制是在调用堆栈顶部有一个额外的条目。但这很容易被忽视。
Here's an example with two custom parameters:
这里有一个具有两个自定义参数的示例:
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}}
);
Example Usage:
使用示例:
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
}
For environments that require a polyfil of setPrototypeOf:
对于需要setPrototypeOf的polyfil的环境:
Object.setPrototypeOf = Object.setPrototypeOf || function (obj, proto) {
obj.__proto__ = proto;
return obj;
};
#11
7
In the above example Error.apply
(also Error.call
) doesn't do anything for me (Firefox 3.6/Chrome 5). A workaround I use is:
在上面的示例错误中。应用(同样错误。调用)对我没有任何帮助(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';
#12
4
As some people have said, it's fairly easy with ES6:
正如一些人所说,ES6很容易:
class CustomError extends Error { }
So I tried that within my app, (Angular, Typescript) and it just didn't work. After some time I've found that the problem is coming from Typescript :O
所以我在我的应用(有棱角的,打字稿的)里试过了,但是没有效果。过了一段时间,我发现问题来自打字稿:O。
See https://github.com/Microsoft/TypeScript/issues/13965
参见https://github.com/Microsoft/TypeScript/issues/13965
It's very disturbing because if you do:
这非常令人不安,因为如果你这样做:
class CustomError extends Error {}
try {
throw new CustomError()
} catch(e) {
if (e instanceof CustomError) {
console.log('Custom error');
} else {
console.log('Basic error');
}
}
In node or directly into your browser it'll display: Custom error
在node或直接进入您的浏览器,它将显示:自定义错误
Try to run that with Typescript in your project on on Typescript playground, it'll display Basic error
...
试着在你的项目中运行打字稿在打字稿操场上,它会显示基本的错误……
The solution is to do the following:
解决办法是:
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;
}
}
#13
3
I just want to add to what others have already stated:
我只是想补充别人已经说过的话:
To make sure that the custom error class shows up properly in the stack trace, you need to set the custom error class's prototype's name property to the custom error class's name property. This is what I mean:
要确保自定义错误类在堆栈跟踪中正确显示,您需要将自定义错误类的原型名称属性设置为自定义错误类的名称属性。这就是我的意思:
CustomError.prototype = Error.prototype;
CustomError.prototype.name = 'CustomError';
So the full example would be:
所以完整的例子是:
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';
When all is said and done, you throw your new exception and it looks like this (I lazily tried this in the chrome dev tools):
当一切都说完了,你抛出一个新的异常,它看起来是这样的(我在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)
#14
3
My 2 cents:
我的2美分。
Why another answer?
a) Because accessing the Error.stack
property (as in some answers) have a large performance penalty.
a)因为访问错误。栈属性(在某些答案中)有很大的性能损失。
b) Because it is only one line.
因为只有一行。
c) Because the solution at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error does not seems to preserve stack info.
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);
};
usage example
使用的例子
http://jsfiddle.net/luciotato/xXyeB/
http://jsfiddle.net/luciotato/xXyeB/
What does it do?
this.__proto__.__proto__
is MyError.prototype.__proto__
, so it is setting the __proto__
FOR ALL INSTANCES of MyError to a specific newly created Error. It keeps MyError class properties and methods and also puts the new Error properties (including .stack) in the __proto__
chain.
this.__proto__。__proto__ MyError.prototype。__proto__,因此它将MyError的所有实例的__proto__设置为一个新创建的特定错误。它保留了MyError类的属性和方法,并在__proto__链中添加了新的错误属性(包括.stack)。
Obvious problem:
You can not have more than one instance of MyError with useful stack info.
使用有用的堆栈信息不能有多个MyError实例。
Do not use this solution if you do not fully understand what this.__proto__.__proto__=
does.
如果你不完全理解这个问题,不要使用这个解决方案。__proto__ =。
#15
2
Since JavaScript Exceptions are difficult to sub-class, I don't sub-class. I just create a new Exception class and use an Error inside of it. I change the Error.name property so that it looks like my custom exception on the console:
由于JavaScript异常很难子类化,所以我不子类化。我只是创建一个新的异常类,并在其中使用一个错误。我更改了Error.name属性,使它看起来像我在控制台的自定义异常:
var InvalidInputError = function(message) {
var error = new Error(message);
error.name = 'InvalidInputError';
return error;
};
The above new exception can be thrown just like a regular Error and it will work as expected, for example:
上面的新异常可以像常规错误一样抛出,它将按照预期工作,例如:
throw new InvalidInputError("Input must be a string");
// Output: Uncaught InvalidInputError: Input must be a string
Caveat: the stack trace is not perfect, as it will bring you to where the new Error is created and not where you throw. This is not a big deal on Chrome because it provides you with a full stack trace directly in the console. But it's more problematic on Firefox, for example.
注意:堆栈跟踪不是完美的,因为它会将您带到创建新错误的位置,而不是您抛出的位置。这对Chrome不是什么大问题,因为它直接在控制台提供完整的堆栈跟踪。但是在火狐上就更成问题了。
#16
1
The way to do this right is to return the result of the apply from the constructor, as well as setting the prototype in the usual complicated javascripty way:
正确的方法是返回构造函数的应用结果,并以通常复杂的javascript方式设置原型:
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 ...>
The only problems with this way of doing it at this point (i've iterated it a bit) are that
这种方法在这一点上唯一的问题是(我已经重复了一点)
- properties other than
stack
andmessage
aren't included inMyError
and - 除了堆栈和消息之外的属性不包括在MyError和
- the stacktrace has an additional line that isn't really necessary.
- stacktrace还有一个不需要的附加行。
The first problem could be fixed by iterating through all the non-enumerable properties of error using the trick in this answer: Is it possible to get the non-enumerable inherited property names of an object?, but this isn't supported by ie<9. The second problem could be solved by tearing out that line in the stack trace, but I'm not sure how to safely do that (maybe just removing the second line of e.stack.toString() ??).
第一个问题可以通过使用这个答案中的技巧遍历错误的所有非可枚举属性来解决:是否可能获得一个对象的非可枚举继承属性名称?,但ie<9不支持这一功能。第二个问题可以通过在堆栈跟踪中删除这一行来解决,但是我不确定如何安全地完成这个操作(可能只是删除e.stack.toString()的第二行)。
#17
1
As pointed out in Mohsen's answer, in ES6 it's possible to extend errors using classes. It's a lot easier and their behavior is more consistent with native errors...but unfortunately it's not a simple matter to use this in the browser if you need to support pre-ES6 browsers. See below for some notes on how that might be implemented, but in the meantime I suggest a relatively simple approach that incorporates some of the best suggestions from other answers:
正如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';
In ES6 it's as simple as:
在ES6中,这很简单:
class CustomError extends Error {}
...and you can detect support for ES6 classes with try {eval('class X{}')
, but you'll get a syntax error if you attempt to include the ES6 version in a script that's loaded by older browsers. So the only way to support all browsers would be to load a separate script dynamically (e.g. via AJAX or eval()
) for browsers that support ES6. A further complication is that eval()
isn't supported in all environments (due to Content Security Policies), which may or may not be a consideration for your project.
…您可以使用try {eval('class X{}')检测对ES6类的支持,但是如果试图将ES6版本包含在由旧浏览器加载的脚本中,则会出现语法错误。因此,支持所有浏览器的唯一方法是动态地为支持ES6的浏览器加载一个独立的脚本(例如通过AJAX或eval())。更复杂的是,eval()在所有环境中都不受支持(由于内容安全策略),这可能是您的项目需要考虑的问题,也可能不是。
So for now, either the first approach above or simply using Error
directly without trying to extend it seems to be the best that can practically be done for code that needs to support non-ES6 browsers.
因此,就目前而言,上面的第一种方法或者直接使用错误而不进行扩展似乎是对需要支持非es6浏览器的代码最好的方法。
There is one other approach that some people might want to consider, which is to use Object.setPrototypeOf()
where available to create an error object that's an instance of your custom error type but which looks and behaves more like a native error in the console (thanks to Ben's answer for the recommendation). Here's my take on that approach: https://gist.github.com/mbrowne/fe45db61cea7858d11be933a998926a8. But given that one day we'll be able to just use ES6, personally I'm not sure the complexity of that approach is worth it.
还有另一个方法,有些人可能想考虑,这是使用Object.setPrototypeOf(),可以创建一个错误对象的自定义错误类型的实例,但外观和行为更像是一个本地错误控制台(由于本推荐的回答)。下面是我对这种方法的看法:https://gist.github.com/mbrowne/fe45db61cea7858d11be933a998926a8。但是考虑到有一天我们可以使用ES6,我个人不确定这种方法的复杂性是否值得。
#18
0
I would take a step back and consider why you want to do that? I think the point is to deal with different errors differently.
我会退一步想想为什么你要这么做?我认为重点是用不同的方法处理不同的错误。
For example, in Python, you can restrict the catch statement to only catch MyValidationError
, and perhaps you want to be able to do something similar in javascript.
例如,在Python中,您可以将catch语句限制为只能捕获MyValidationError,并且您可能希望能够在javascript中执行类似的操作。
catch (MyValidationError e) {
....
}
You can't do this in javascript. There's only going to be one catch block. You're supposed to use an if statement on the error to determine its type.
在javascript中不能这样做。只有一个抓块。您应该对错误使用if语句来确定其类型。
catch(e) { if(isMyValidationError(e)) { ... } else { // maybe rethrow? throw e; } }
catch(e) {if(isMyValidationError(e)){…{//可能重新抛出?把e;} }
I think I would instead throw a raw object with a type, message, and any other properties you see fit.
我认为我应该抛出一个带有类型、消息和任何其他您认为合适的属性的原始对象。
throw { type: "validation", message: "Invalid timestamp" }
And when you catch the error:
当你发现错误时
catch(e) {
if(e.type === "validation") {
// handle error
}
// re-throw, or whatever else
}
#19
0
Custom Error Decorator
This is based on George Bailey's answer, but extends and simplifies the original idea. It is written in CoffeeScript, but is easy to convert to JavaScript. The idea is extend Bailey's custom error with a decorator that wraps it, allowing you to create new custom errors easily.
这是基于George Bailey的回答,但是扩展并简化了最初的想法。它是用CoffeeScript写的,但是很容易转换成JavaScript。其思想是使用包装器扩展Bailey的自定义错误,允许您轻松创建新的自定义错误。
Note: This will only work in V8. There is no support for Error.captureStackTrace
in other environments.
注意:这只适用于V8。没有错误的支持。captureStackTrace在其他环境中。
Define
The decorator takes a name for the error type, and returns a function that takes an error message, and encloses the error name.
decorator为错误类型取一个名称,并返回一个接收错误消息的函数,并将错误名称封装起来。
CoreError = (@message) ->
@constructor.prototype.__proto__ = Error.prototype
Error.captureStackTrace @, @constructor
@name = @constructor.name
BaseError = (type) ->
(message) -> new CoreError "#{ type }Error: #{ message }"
Use
Now it is simple to create new error types.
现在很容易创建新的错误类型。
StorageError = BaseError "Storage"
SignatureError = BaseError "Signature"
For fun, you could now define a function that throws a SignatureError
if it is called with too many args.
有趣的是,您现在可以定义一个函数,如果用太多的args调用该函数,它将抛出一个SignatureError。
f = -> throw SignatureError "too many args" if arguments.length
This has been tested pretty well and seems to work perfectly on V8, maintaing the traceback, position etc.
这已经经过了很好的测试,似乎在V8上工作得很好,维护回溯,位置等等。
Note: Using new
is optional when constructing a custom error.
注意:在构造定制错误时使用new是可选的。
#1
175
The only standard field Error object has is the message
property. (See MDN, or EcmaScript Language Specification, section 15.11) Everything else is platform specific.
唯一的标准字段错误对象是message属性。(参见MDN或EcmaScript语言规范第15.11节)其他内容都是特定于平台的。
Mosts environments set the stack
property, but fileName
and lineNumber
are practically useless to be used in inheritance.
Mosts环境设置堆栈属性,但是文件名和lineNumber在继承中实际上是无用的。
So, the minimalistic approach is:
所以,极简主义的方法是:
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
You could sniff the stack, unshift unwanted elements from it and extract information like fileName and lineNumber, but doing so requires information about the platform JavaScript is currently running upon. Most cases that is unnecessary -- and you can do it in post-mortem if you really want.
您可以嗅探堆栈,从堆栈中移出不需要的元素,并提取文件名和lineNumber等信息,但是这样做需要关于当前运行的平台JavaScript的信息。大多数情况下这是不必要的——如果你真的想做,你可以在尸检后再做。
Safari is a notable exception. There is no stack
property, but the throw
keyword sets sourceURL
and line
properties of the object that is being thrown. Those things are guaranteed to be correct.
Safari是个明显的例外。没有堆栈属性,但是throw关键字设置正在抛出的对象的sourceURL和行属性。这些东西肯定是正确的。
Test cases I used can be found here: JavaScript self-made Error object comparison.
我使用的测试用例可以在这里找到:JavaScript自制错误对象比较。
#2
109
In ES6:
在ES6:
class MyError extends Error {
constructor(message) {
super(message);
this.name = 'MyError';
}
}
源
#3
38
Edit: Please read comments. It turns out this only works well in V8 (Chrome / Node.JS) My intent was to provide a cross-browser solution, which would work in all browsers, and provide stack trace where support is there.
编辑:请阅读评论。我的目的是提供一个跨浏览器的解决方案,它可以在所有浏览器中工作,并在支持的地方提供堆栈跟踪。
Edit: I made this Community Wiki to allow for more editing.
编辑:我创建这个社区Wiki是为了允许更多的编辑。
Solution for V8 (Chrome / Node.JS), works in Firefox, and can be modified to function mostly correctly in IE. (see end of post)
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
}
Original post on "Show me the code !"
原来的帖子“给我看代码!”
Short version:
短版:
function UserError(message) {
this.constructor.prototype.__proto__ = Error.prototype
Error.captureStackTrace(this, this.constructor)
this.name = this.constructor.name
this.message = message
}
I keep this.constructor.prototype.__proto__ = Error.prototype
inside the function to keep all the code together. But you can also replace this.constructor
with UserError
and that allows you to move the code to outside the function, so it only gets called once.
我一直this.constructor.prototype。__proto__ =错误。在函数内部的原型,以保持所有的代码在一起。但是你也可以替换它。带有UserError的构造函数,允许您将代码移到函数之外,因此只调用一次。
If you go that route, make sure you call that line before the first time you throw UserError
.
如果您走那条路线,请确保在第一次抛出UserError之前调用该线。
That caveat does not apply the function, because functions are created first, no matter the order. Thus, you can move the function to the end of the file, without a problem.
这个警告不应用函数,因为函数是先创建的,不管顺序如何。因此,可以毫无问题地将函数移动到文件的末尾。
Browser Compatibility
浏览器兼容性
Works in Firefox and Chrome (and Node.JS) and fills all promises.
适用于Firefox和Chrome(以及Node.JS),并满足所有的承诺。
Internet Explorer fails in the following
Internet Explorer在以下方面失败
-
Errors do not have
err.stack
to begin with, so "it's not my fault".错误没有错。堆栈开始,所以“这不是我的错”。
-
Error.captureStackTrace(this, this.constructor)
does not exist so you need to do something else like错误。captureStackTrace(this, this.构造函数)不存在,所以需要做一些类似的事情
if(Error.captureStackTrace) // AKA if not IE Error.captureStackTrace(this, this.constructor)
-
toString
ceases to exist when you subclassError
. So you also need to add.当您子类化错误时,toString就不存在了。所以你也需要添加。
else this.toString = function () { return this.name + ': ' + this.message }
-
IE will not consider
UserError
to be aninstanceof Error
unless you run the following some time before youthrow UserError
IE不会认为UserError是一个instanceof错误,除非您在抛出UserError之前运行以下代码
UserError.prototype = Error.prototype
#4
24
To avoid the boilerplate for every different type of error, I combined the wisdom of some of the solutions into a createErrorType
function:
为了避免出现每种不同类型的错误,我将一些解决方案的智慧结合到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;
}
Then you can define new error types easily as follows:
然后您可以轻松定义新的错误类型如下:
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';
});
#5
18
Crescent Fresh's answer highly-voted answer is misleading. Though his warnings are invalid, there are other limitations that he doesn't address.
新月形新鲜的回答高投票的回答是误导。尽管他的警告是无效的,但还有其他一些限制他没有提及。
First, the reasoning in Crescent's "Caveats:" paragraph doesn't make sense. The explanation implies that coding "a bunch of if (error instanceof MyError) else ..." is somehow burdensome or verbose compared to multiple catch statements. Multiple instanceof statements in a single catch block are just as concise as multiple catch statements-- clean and concise code without any tricks. This is a great way to emulate Java's great throwable-subtype-specific error handling.
首先,新月的“警告”中的推理是没有意义的。这种解释意味着,与多个catch语句相比,编写“一堆if (error instanceof MyError) else……”可能有些麻烦或冗长。一个catch块中的多个instanceof语句与多个catch语句一样简洁——干净简洁的代码没有任何技巧。这是一种很好的方法来模拟Java出色的、特定于抛出子类型的错误处理。
WRT "appears the message property of the subclass does not get set", that is not the case if you use a properly constructed Error subclass. To make your own ErrorX Error subclass, just copy the code block beginning with "var MyError =", changing the one word "MyError" to "ErrorX". (If you want to add custom methods to your subclass, follow the sample text).
WRT“出现子类的消息属性未被设置”,如果您使用一个正确构造的错误子类,则不是这种情况。要创建自己的ErrorX错误子类,只需复制以“var MyError =”开头的代码块,将一个单词“MyError”更改为“ErrorX”。(如果希望向子类添加自定义方法,请遵循示例文本)。
The real and significant limitation of JavaScript error subclassing is that for JavaScript implementations or debuggers that track and report on stack trace and location-of-instantiation, like FireFox, a location in your own Error subclass implementation will be recorded as the instantiation point of the class, whereas if you used a direct Error, it would be the location where you ran "new Error(...)"). IE users would probably never notice, but users of Fire Bug on FF will see useless file name and line number values reported alongside these Errors, and will have to drill down on the stack trace to element #1 to find the real instantiation location.
JavaScript错误子类化的真实和重要的限制是JavaScript实现或调试器,跟踪和报告在堆栈跟踪和location-of-instantiation,像FireFox,位置在你自己的错误子类实现将记录为实例化的类,而如果使用直接错误,它将是你的位置跑“新的错误(…)”)。IE用户可能永远不会注意到,但是FF上的Fire Bug用户将会看到无用的文件名和行号值与这些错误一起报告,并且必须深入到堆栈跟踪到元素#1以找到真正的实例化位置。
#6
17
In 2017, I think this is the best way; that supports IE9+ and modern browsers.
在2017年,我认为这是最好的方式;它支持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 }
});
}
Also beware that __proto__
property is deprecated which is widely used in other answers.
还要注意__proto__属性已被弃用,这在其他答案中被广泛使用。
Note: See discussion in the comments and @MattBrowne's gist.
注:请参阅评论和@MattBrowne的要点讨论。
UPDATE: Added constructor
assignment. See this test for comparison on different implementations. Test module here.
更新:添加构造函数赋值。有关不同实现的比较,请参见此测试。测试模块。
#7
16
In short:
简而言之:
-
If you are using ES6 without transpilers:
如果你使用的是ES6,没有运输工具:
class CustomError extends Error { /* ... */}
-
If you are using Babel transpiler:
如果你使用的是Babel transiler:
Option 1: use babel-plugin-transform-builtin-extend
选项1:使用babel-plugin-transform-builtin-extend
Option 2: do it yourself (inspired from that same library)
选项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);
-
If you are using pure ES5:
如果你使用纯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; }
-
Alternative: use Classtrophobic framework
选择:使用Classtrophobic框架
Explanation:
解释:
Why extending the Error class using ES6 and Babel is a problem?
为什么使用ES6和Babel扩展错误类是一个问题?
Because an instance of CustomError is not anymore recognized as such.
因为CustomError的实例不再是这样识别的。
class CustomError extends Error {}
console.log(new CustomError('test') instanceof Error);// true
console.log(new CustomError('test') instanceof CustomError);// false
In fact, from the official documentation of Babel, you cannot extend any built-in JavaScript classes such as Date
, Array
, DOM
or Error
.
事实上,从Babel的官方文档中,您不能扩展任何内置的JavaScript类,比如Date、Array、DOM或Error。
The issue is described here:
问题如下:
- Native extends breaks HTMLELement, Array, and others
- 本机扩展中断HTMLELement、Array和其他
- an object of The class which is extends by base type like Array,Number,Object,String or Error is not instanceof this class
- 由基类型扩展的类的对象(如数组、数字、对象、字符串或错误)不是该类的实例
What about the other SO answers?
那么另一个呢?
All the given answers fix the instanceof
issue but you lose the regular error console.log
:
所有给定的答案都修复了实例问题,但是您失去了常规的错误控制。
console.log(new CustomError('test'));
// output:
// CustomError {name: "MyError", message: "test", stack: "Error↵ at CustomError (<anonymous>:4:19)↵ at <anonymous>:1:5"}
Whereas using the method mentioned above, not only you fix the instanceof
issue but you also keep the regular error console.log
:
使用上面提到的方法,不仅可以修复instanceof问题,还可以保持常规的error console.log:
console.log(new CustomError('test'));
// output:
// Error: test
// at CustomError (<anonymous>:2:32)
// at <anonymous>:1:5
#8
14
For the sake of completeness -- just because none of the previous answers mentioned this method -- if you are working with Node.js and don't have to care about browser compatibility, the desired effect is pretty easy to achieve with the built in inherits
of the util
module (official docs here).
为了完整性起见——仅仅因为前面的答案中没有提到这个方法——如果您使用的是Node。不需要关心浏览器的兼容性,通过继承util模块(这里的官方文档)来实现所需的效果非常容易。
For example, let's suppose you want to create a custom error class that takes an error code as the first argument and the error message as the second argument:
例如,假设您想创建一个自定义错误类,它将错误代码作为第一个参数,错误消息作为第二个参数:
file custom-error.js:
文件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;
Now you can instantiate and pass/throw your 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!');
Note that, with this snippet, the stack trace will have the correct file name and line, and the error instance will have the correct name!
注意,使用此代码段,堆栈跟踪将具有正确的文件名和行,错误实例将具有正确的名称!
This happens due to the usage of the captureStackTrace
method, which creates a stack
property on the target object (in this case, the CustomError
being instantiated). For more details about how it works, check the documentation here.
这是由于使用captureStackTrace方法导致的,该方法在目标对象上创建堆栈属性(在本例中,正在实例化CustomError)。有关它如何工作的更多细节,请查看这里的文档。
#9
11
How about this solution?
该解决方案如何?
Instead of throwing your custom Error using:
不要使用:
throw new MyError("Oops!");
You would wrap the Error object (kind of like a Decorator):
您将封装错误对象(类似于Decorator):
throw new MyError(Error("Oops!"));
This makes sure all of the attributes are correct, such as the stack, fileName lineNumber, et cetera.
这确保所有属性都是正确的,例如堆栈、文件名lineNumber等等。
All you have to do then is either copy over the attributes, or define getters for them. Here is an example using getters (IE9):
然后,您所要做的就是复制这些属性,或者为它们定义getter。这里有一个使用getters (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();
};
#10
9
My solution is more simple than the other answers provided and doesn't have the downsides.
我的解决方案比提供的其他答案更简单,没有缺点。
It preserves the Error prototype chain and all properties on Error without needing specific knowledge of them. It's been tested in Chrome, Firefox, Node, and IE11.
它保留了错误原型链和所有的错误属性,而不需要特定的知识。它已经在Chrome、Firefox、Node和IE11中进行了测试。
The only limitation is an extra entry at the top of the call stack. But that is easily ignored.
唯一的限制是在调用堆栈顶部有一个额外的条目。但这很容易被忽视。
Here's an example with two custom parameters:
这里有一个具有两个自定义参数的示例:
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}}
);
Example Usage:
使用示例:
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
}
For environments that require a polyfil of setPrototypeOf:
对于需要setPrototypeOf的polyfil的环境:
Object.setPrototypeOf = Object.setPrototypeOf || function (obj, proto) {
obj.__proto__ = proto;
return obj;
};
#11
7
In the above example Error.apply
(also Error.call
) doesn't do anything for me (Firefox 3.6/Chrome 5). A workaround I use is:
在上面的示例错误中。应用(同样错误。调用)对我没有任何帮助(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';
#12
4
As some people have said, it's fairly easy with ES6:
正如一些人所说,ES6很容易:
class CustomError extends Error { }
So I tried that within my app, (Angular, Typescript) and it just didn't work. After some time I've found that the problem is coming from Typescript :O
所以我在我的应用(有棱角的,打字稿的)里试过了,但是没有效果。过了一段时间,我发现问题来自打字稿:O。
See https://github.com/Microsoft/TypeScript/issues/13965
参见https://github.com/Microsoft/TypeScript/issues/13965
It's very disturbing because if you do:
这非常令人不安,因为如果你这样做:
class CustomError extends Error {}
try {
throw new CustomError()
} catch(e) {
if (e instanceof CustomError) {
console.log('Custom error');
} else {
console.log('Basic error');
}
}
In node or directly into your browser it'll display: Custom error
在node或直接进入您的浏览器,它将显示:自定义错误
Try to run that with Typescript in your project on on Typescript playground, it'll display Basic error
...
试着在你的项目中运行打字稿在打字稿操场上,它会显示基本的错误……
The solution is to do the following:
解决办法是:
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;
}
}
#13
3
I just want to add to what others have already stated:
我只是想补充别人已经说过的话:
To make sure that the custom error class shows up properly in the stack trace, you need to set the custom error class's prototype's name property to the custom error class's name property. This is what I mean:
要确保自定义错误类在堆栈跟踪中正确显示,您需要将自定义错误类的原型名称属性设置为自定义错误类的名称属性。这就是我的意思:
CustomError.prototype = Error.prototype;
CustomError.prototype.name = 'CustomError';
So the full example would be:
所以完整的例子是:
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';
When all is said and done, you throw your new exception and it looks like this (I lazily tried this in the chrome dev tools):
当一切都说完了,你抛出一个新的异常,它看起来是这样的(我在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)
#14
3
My 2 cents:
我的2美分。
Why another answer?
a) Because accessing the Error.stack
property (as in some answers) have a large performance penalty.
a)因为访问错误。栈属性(在某些答案中)有很大的性能损失。
b) Because it is only one line.
因为只有一行。
c) Because the solution at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error does not seems to preserve stack info.
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);
};
usage example
使用的例子
http://jsfiddle.net/luciotato/xXyeB/
http://jsfiddle.net/luciotato/xXyeB/
What does it do?
this.__proto__.__proto__
is MyError.prototype.__proto__
, so it is setting the __proto__
FOR ALL INSTANCES of MyError to a specific newly created Error. It keeps MyError class properties and methods and also puts the new Error properties (including .stack) in the __proto__
chain.
this.__proto__。__proto__ MyError.prototype。__proto__,因此它将MyError的所有实例的__proto__设置为一个新创建的特定错误。它保留了MyError类的属性和方法,并在__proto__链中添加了新的错误属性(包括.stack)。
Obvious problem:
You can not have more than one instance of MyError with useful stack info.
使用有用的堆栈信息不能有多个MyError实例。
Do not use this solution if you do not fully understand what this.__proto__.__proto__=
does.
如果你不完全理解这个问题,不要使用这个解决方案。__proto__ =。
#15
2
Since JavaScript Exceptions are difficult to sub-class, I don't sub-class. I just create a new Exception class and use an Error inside of it. I change the Error.name property so that it looks like my custom exception on the console:
由于JavaScript异常很难子类化,所以我不子类化。我只是创建一个新的异常类,并在其中使用一个错误。我更改了Error.name属性,使它看起来像我在控制台的自定义异常:
var InvalidInputError = function(message) {
var error = new Error(message);
error.name = 'InvalidInputError';
return error;
};
The above new exception can be thrown just like a regular Error and it will work as expected, for example:
上面的新异常可以像常规错误一样抛出,它将按照预期工作,例如:
throw new InvalidInputError("Input must be a string");
// Output: Uncaught InvalidInputError: Input must be a string
Caveat: the stack trace is not perfect, as it will bring you to where the new Error is created and not where you throw. This is not a big deal on Chrome because it provides you with a full stack trace directly in the console. But it's more problematic on Firefox, for example.
注意:堆栈跟踪不是完美的,因为它会将您带到创建新错误的位置,而不是您抛出的位置。这对Chrome不是什么大问题,因为它直接在控制台提供完整的堆栈跟踪。但是在火狐上就更成问题了。
#16
1
The way to do this right is to return the result of the apply from the constructor, as well as setting the prototype in the usual complicated javascripty way:
正确的方法是返回构造函数的应用结果,并以通常复杂的javascript方式设置原型:
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 ...>
The only problems with this way of doing it at this point (i've iterated it a bit) are that
这种方法在这一点上唯一的问题是(我已经重复了一点)
- properties other than
stack
andmessage
aren't included inMyError
and - 除了堆栈和消息之外的属性不包括在MyError和
- the stacktrace has an additional line that isn't really necessary.
- stacktrace还有一个不需要的附加行。
The first problem could be fixed by iterating through all the non-enumerable properties of error using the trick in this answer: Is it possible to get the non-enumerable inherited property names of an object?, but this isn't supported by ie<9. The second problem could be solved by tearing out that line in the stack trace, but I'm not sure how to safely do that (maybe just removing the second line of e.stack.toString() ??).
第一个问题可以通过使用这个答案中的技巧遍历错误的所有非可枚举属性来解决:是否可能获得一个对象的非可枚举继承属性名称?,但ie<9不支持这一功能。第二个问题可以通过在堆栈跟踪中删除这一行来解决,但是我不确定如何安全地完成这个操作(可能只是删除e.stack.toString()的第二行)。
#17
1
As pointed out in Mohsen's answer, in ES6 it's possible to extend errors using classes. It's a lot easier and their behavior is more consistent with native errors...but unfortunately it's not a simple matter to use this in the browser if you need to support pre-ES6 browsers. See below for some notes on how that might be implemented, but in the meantime I suggest a relatively simple approach that incorporates some of the best suggestions from other answers:
正如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';
In ES6 it's as simple as:
在ES6中,这很简单:
class CustomError extends Error {}
...and you can detect support for ES6 classes with try {eval('class X{}')
, but you'll get a syntax error if you attempt to include the ES6 version in a script that's loaded by older browsers. So the only way to support all browsers would be to load a separate script dynamically (e.g. via AJAX or eval()
) for browsers that support ES6. A further complication is that eval()
isn't supported in all environments (due to Content Security Policies), which may or may not be a consideration for your project.
…您可以使用try {eval('class X{}')检测对ES6类的支持,但是如果试图将ES6版本包含在由旧浏览器加载的脚本中,则会出现语法错误。因此,支持所有浏览器的唯一方法是动态地为支持ES6的浏览器加载一个独立的脚本(例如通过AJAX或eval())。更复杂的是,eval()在所有环境中都不受支持(由于内容安全策略),这可能是您的项目需要考虑的问题,也可能不是。
So for now, either the first approach above or simply using Error
directly without trying to extend it seems to be the best that can practically be done for code that needs to support non-ES6 browsers.
因此,就目前而言,上面的第一种方法或者直接使用错误而不进行扩展似乎是对需要支持非es6浏览器的代码最好的方法。
There is one other approach that some people might want to consider, which is to use Object.setPrototypeOf()
where available to create an error object that's an instance of your custom error type but which looks and behaves more like a native error in the console (thanks to Ben's answer for the recommendation). Here's my take on that approach: https://gist.github.com/mbrowne/fe45db61cea7858d11be933a998926a8. But given that one day we'll be able to just use ES6, personally I'm not sure the complexity of that approach is worth it.
还有另一个方法,有些人可能想考虑,这是使用Object.setPrototypeOf(),可以创建一个错误对象的自定义错误类型的实例,但外观和行为更像是一个本地错误控制台(由于本推荐的回答)。下面是我对这种方法的看法:https://gist.github.com/mbrowne/fe45db61cea7858d11be933a998926a8。但是考虑到有一天我们可以使用ES6,我个人不确定这种方法的复杂性是否值得。
#18
0
I would take a step back and consider why you want to do that? I think the point is to deal with different errors differently.
我会退一步想想为什么你要这么做?我认为重点是用不同的方法处理不同的错误。
For example, in Python, you can restrict the catch statement to only catch MyValidationError
, and perhaps you want to be able to do something similar in javascript.
例如,在Python中,您可以将catch语句限制为只能捕获MyValidationError,并且您可能希望能够在javascript中执行类似的操作。
catch (MyValidationError e) {
....
}
You can't do this in javascript. There's only going to be one catch block. You're supposed to use an if statement on the error to determine its type.
在javascript中不能这样做。只有一个抓块。您应该对错误使用if语句来确定其类型。
catch(e) { if(isMyValidationError(e)) { ... } else { // maybe rethrow? throw e; } }
catch(e) {if(isMyValidationError(e)){…{//可能重新抛出?把e;} }
I think I would instead throw a raw object with a type, message, and any other properties you see fit.
我认为我应该抛出一个带有类型、消息和任何其他您认为合适的属性的原始对象。
throw { type: "validation", message: "Invalid timestamp" }
And when you catch the error:
当你发现错误时
catch(e) {
if(e.type === "validation") {
// handle error
}
// re-throw, or whatever else
}
#19
0
Custom Error Decorator
This is based on George Bailey's answer, but extends and simplifies the original idea. It is written in CoffeeScript, but is easy to convert to JavaScript. The idea is extend Bailey's custom error with a decorator that wraps it, allowing you to create new custom errors easily.
这是基于George Bailey的回答,但是扩展并简化了最初的想法。它是用CoffeeScript写的,但是很容易转换成JavaScript。其思想是使用包装器扩展Bailey的自定义错误,允许您轻松创建新的自定义错误。
Note: This will only work in V8. There is no support for Error.captureStackTrace
in other environments.
注意:这只适用于V8。没有错误的支持。captureStackTrace在其他环境中。
Define
The decorator takes a name for the error type, and returns a function that takes an error message, and encloses the error name.
decorator为错误类型取一个名称,并返回一个接收错误消息的函数,并将错误名称封装起来。
CoreError = (@message) ->
@constructor.prototype.__proto__ = Error.prototype
Error.captureStackTrace @, @constructor
@name = @constructor.name
BaseError = (type) ->
(message) -> new CoreError "#{ type }Error: #{ message }"
Use
Now it is simple to create new error types.
现在很容易创建新的错误类型。
StorageError = BaseError "Storage"
SignatureError = BaseError "Signature"
For fun, you could now define a function that throws a SignatureError
if it is called with too many args.
有趣的是,您现在可以定义一个函数,如果用太多的args调用该函数,它将抛出一个SignatureError。
f = -> throw SignatureError "too many args" if arguments.length
This has been tested pretty well and seems to work perfectly on V8, maintaing the traceback, position etc.
这已经经过了很好的测试,似乎在V8上工作得很好,维护回溯,位置等等。
Note: Using new
is optional when constructing a custom error.
注意:在构造定制错误时使用new是可选的。