我想在我的JS代码中抛出一些东西,我想让它们成为instanceof Error,但我也想让它们成为其他东西。
在Python中,通常会子类化Exception。
在JS中应该做什么?
我想在我的JS代码中抛出一些东西,我想让它们成为instanceof Error,但我也想让它们成为其他东西。
在Python中,通常会子类化Exception。
在JS中应该做什么?
当前回答
Crescent Fresh的答案是误导性的。尽管他的警告是无效的,但还有其他一些限制他没有提到。
首先,Crescent的“警告:”段落中的推理没有意义。这种解释意味着,与多个catch语句相比,编码“一堆if (error instanceof MyError) else…”在某种程度上是累赘或冗长的。单个catch块中的多个instanceof语句与多个catch语句一样简洁——干净简洁的代码,没有任何技巧。这是模拟Java出色的特定于可抛出子类型的错误处理的好方法。
WRT“显示子类的消息属性没有得到设置”,如果使用正确构造的Error子类,则不会出现这种情况。要创建自己的ErrorX Error子类,只需复制以"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.
其他回答
在2018年,我认为这是最好的方式;支持IE9+和现代浏览器。
更新:请参阅此测试和repo以了解不同实现的比较。
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__属性已弃用,该属性在其他答案中广泛使用。
简而言之:
如果你使用的是不带转译器的ES6: 类CustomError扩展错误{/*…* /} 如果你正在使用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);
If you are using pure 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
解释:
为什么使用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。
问题描述如下:
本机扩展破坏HTMLELement, Array和其他 类的一个对象,它扩展了基类型如数组,数字,对象,字符串或错误不是这个类的实例
那么其他的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问题,还可以保留常规的error console.log:
console.log(new CustomError('test'));
// output:
// Error: test
// at CustomError (<anonymous>:2:32)
// at <anonymous>:1:5
因为JavaScript异常很难子类化,所以我不子类化。我只是创建了一个新的Exception类,并在其中使用一个Error。我修改了Error.name属性,使它看起来像我在控制台上的自定义异常:
var InvalidInputError = function(message) {
var error = new Error(message);
error.name = 'InvalidInputError';
return error;
};
上面的new异常可以像普通的Error一样被抛出,它会像预期的那样工作,例如:
throw new InvalidInputError("Input must be a string");
// Output: Uncaught InvalidInputError: Input must be a string
注意:堆栈跟踪并不是完美的,因为它只会把你带到创建新错误的地方,而不是你抛出错误的地方。这在Chrome上不是什么大问题,因为它直接在控制台中为您提供了完整的堆栈跟踪。但在Firefox上问题就更多了。
这并不复杂,但我个人认为这是扩展错误的最简单方法。
export default class ExtendableError extends Error {
constructor(message) {
super(message);
this.name = this.constructor.name;
}
}
创建一个名为ExtendableError的实用程序类。这个实用程序类的目的是与普通的Error类类似,但在默认情况下将name属性更改为类的名称,因此很容易扩展错误。
现在,如果您想扩展一个错误,它只需要一行。
class MyError extends ExtendableError {}
我想再补充一下大家已经说过的话:
为了确保自定义错误类在堆栈跟踪中正确显示,您需要将自定义错误类的原型的name属性设置为自定义错误类的name属性。 我的意思是:
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)