我想在我的JS代码中抛出一些东西,我想让它们成为instanceof Error,但我也想让它们成为其他东西。
在Python中,通常会子类化Exception。
在JS中应该做什么?
我想在我的JS代码中抛出一些东西,我想让它们成为instanceof Error,但我也想让它们成为其他东西。
在Python中,通常会子类化Exception。
在JS中应该做什么?
当前回答
我的观点是:
为什么又是另一个答案?
a)因为访问错误。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__,所以它为所有实例设置__proto__ MyError的一个特定的新创建的错误。它保留MyError类的属性和方法,并将新的Error属性(包括.stack)放在__proto__链中。
明显的问题:
你不能有一个以上的MyError实例和有用的堆栈信息。
如果你不完全理解this.__proto__。__proto__ =。
其他回答
正确的方法是从构造函数返回apply的结果,以及以通常复杂的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 ...>
在这一点上,这种方法唯一的问题(我已经重复了一点)是
除了stack和message之外的属性不包含在MyError和 堆栈跟踪有一个额外的行,这并不是真正必要的。
第一个问题可以通过使用以下回答中的技巧遍历所有不可枚举的error属性来解决:是否可能获得对象的不可枚举继承属性名?,但ie<9不支持。第二个问题可以通过在堆栈跟踪中删除这一行来解决,但我不确定如何安全地做到这一点(可能只是删除e.stack.toString()的第二行??)
在上面的例子中。apply (also 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';
编辑:请阅读评论。我的目的是提供一个跨浏览器的解决方案,它可以在所有浏览器中工作,并在支持的地方提供堆栈跟踪。
编辑:我创建这个社区维基是为了允许更多的编辑。
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
}
我保留这个。构造器。原型。__proto__ =错误。函数中的原型将所有代码保存在一起。但是你也可以替换这个。使用UserError构造函数,这允许你将代码移到函数之外,所以它只被调用一次。
如果您走那条路,请确保在第一次抛出UserError之前调用该行。
这个警告并不适用于函数,因为函数是先创建的,不管顺序如何。因此,您可以将函数移动到文件的末尾,没有任何问题。
浏览器兼容性
工作在Firefox和Chrome(和Node.JS),并满足所有承诺。
ie浏览器出现如下问题
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 if(Error.captureStackTrace) // AKA if not IE Error.captureStackTrace(this, this.constructor) toString ceases to exist when you subclass Error. So you also need to add. else this.toString = function () { return this.name + ': ' + this.message } IE will not consider UserError to be an instanceof Error unless you run the following some time before you throw UserError UserError.prototype = Error.prototype
Error对象拥有的唯一标准字段是message属性。(参见MDN或EcmaScript语言规范,第15.11节)其他一切都是特定于平台的。
大多数环境都设置了堆栈属性,但是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
您可以嗅探堆栈,从堆栈中移除不需要的元素,并提取文件名和lineNumber等信息,但这样做需要有关JavaScript当前运行的平台的信息。大多数情况下,这是不必要的——如果你真的想,你可以在事后进行分析。
Safari是个明显的例外。没有堆栈属性,但是throw关键字设置了被抛出对象的sourceURL和line属性。这些东西肯定是正确的。
我使用的测试用例可以在这里找到:JavaScript自制错误对象比较。
我建议的解决方案是使用error的.name属性来区分错误类型,而不是instancof
这并没有完全回答问题,但我认为这是一个合理的解决方案,至少在某些情况下。
我所看到的能够拥有一个instanceof CustomError的好处是,您可以在promise catch处理程序中进行自定义处理。
例如:
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
})