我想在我的 JS 代码中抛出一些东西,我希望它们成为 instanceof Error,但我也想让它们成为其他东西。
在 Python 中,通常会继承 Exception。
在 JS 中做什么合适?
我想在我的 JS 代码中抛出一些东西,我希望它们成为 instanceof Error,但我也想让它们成为其他东西。
在 Python 中,通常会继承 Exception。
在 JS 中做什么合适?
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
您可以嗅探堆栈,从中移除不需要的元素并提取文件名和行号等信息,但这样做需要有关 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
在函数内部以将所有代码保存在一起。但是你也可以this.constructor
用UserError
and替换,这允许你将代码移到函数之外,所以它只被调用一次。
如果您走那条路线,请确保在第一次 throw之前调用该线路UserError
。
该警告不适用于该函数,因为无论顺序如何,首先创建函数。因此,您可以将该函数移动到文件末尾,而不会出现问题。
浏览器兼容性
适用于 Firefox 和 Chrome(以及 Node.JS)并实现所有Promise。
Internet Explorer 在以下情况下失败
错误不一定err.stack
要开始,所以“这不是我的错”。
Error.captureStackTrace(this, this.constructor)
不存在所以你需要做一些其他的事情
if(Error.captureStackTrace) // AKA if not IE
Error.captureStackTrace(this, this.constructor)
toString
当你子类化时不复存在Error
。所以你还需要添加。
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';
});