我正在开发一个具有不同类型错误、服务和域概念的复杂应用程序。
为了投掷";对象";错误,我想到了两种不同的方法:
- 将
Object.assign()
应用于Error对象(如果我只需要抛出一个或几个遵循此形式的错误,这是一个简单的选项(:
function f() {
const err = new Error();
Object.assign(err, {
name: "ServiceError",
code: "service/some-string-code",
message: "Some message",
});
throw err;
}
try {
f();
} catch(err) {
console.log(err instanceof Error);
}
- 创建自定义错误(扩展Error类(
class MyServiceError extends Error {
constructor(code, message) {
super(message);
this.name = "ServiceError";
this.code = code;
}
}
function f() {
const err = new MyServiceError("service/some-string-code", "Some message");
throw err;
}
try {
f();
} catch(err) {
console.log(err instanceof Error);
console.log(err instanceof MyServiceError);
}
两者之间的利弊是什么;自定义错误定义";。
此外,如果我选择第二种方法,我似乎需要为不同的域概念、服务等创建多个CustomError
类,以实现对称代码和干净的架构。。。(??(反过来,我认为这是在重新发明轮子并添加不必要的代码,因为也许并不是应用程序的所有概念都需要自定义类型的异常。
这两种做法在JavaScript中都有效吗?
注意:抛出对象或字符串或类似的东西对我来说真的很糟糕,因为我们无法获得堆栈跟踪、验证实例等。
// This seems bad to me. Isn't it an anti-pattern?
throw {
code: "",
message: "",
name: ""
}
Object.assign方法的健壮性较差,更像是一种黑客攻击,最好创建自定义错误类。关于SO.已经有了深入的讨论
如果您想使用额外的字段,最多可以为内部错误引入2-3个自定义类,但即使这样也常常会有些过头:
NetworkError
的一个,带有位置、路径和状态- 一个用于具有组件和有问题的数据状态的
UiError
,可能还有一个用于i18n的消息代码 - 和一个通用
RuntimeError
或类似物,用于未知病例
对每个潜在事件都有一个错误类是没有意义的。与Java不同,JavaScript中没有检查异常,目标是在不过度设计的情况下,只拥有足够的数据来解决问题。如果你能有意义地捕获并在对话框中显示比message
字符串所能容纳的更多的数据,那就去做吧
设计自定义错误时,请从处理和显示这些信息的位置和方式开始。然后看看你是否可以轻松地将这些数据收集到你扔的地方。如果你没有全局错误对话框或集中的错误报告,也许只有默认的错误就足够了,你可以将所有数据放入消息中。
有一种特殊情况,当您希望使用错误作为控制逻辑的手段时。尽量避免,JavaScript非常灵活,不使用throw
作为让上层选择不同执行路径的方式。然而,它有时被用来重新尝试网络请求,然后它应该有足够的数据。
内置错误对象已经有以下字段:
- 名称
- 消息
- 烟囱
在每个错误中,stack
和message
是帮助解决问题的两条关键信息。因此,当你重新投掷它时,使用这样的东西(对于所有非IE(是很重要的:
catch (err) {
throw new Error('New error message with added info', { cause: err });
}
最后,它有助于检查其他人在做什么:
- GraphQL的GraphQLError
- VueJS中的错误处理挂钩(它没有自定义错误(
而且,JavaScript不仅有Error
,还有:
- 评估错误
- RangeError
- ReferenceError
- SyntaxError
- TypeError
- URI错误
- 聚合错误
您也可以在适当的时候投掷它们。
请注意,大多数处理视图的UI框架都没有自定义错误类,也不需要。