错误类型与自定义错误
内置错误类型各表示什么?
| 类型 | 含义 |
|---|---|
Error | 基类型 |
InternalError | 引擎内部错误 |
EvalError | eval() 相关错误 |
RangeError | 数值越界 |
ReferenceError | 找不到引用 |
SyntaxError | 语法错误 (如 eval() 中的非法语法) |
TypeError | 变量不是预期类型 |
URIError | encodeURI()/decodeURI() 相关错误 |
错误对象包含哪些属性?
name: 错误名, 如"ReferenceError";message: 人类可读的错误说明;stack: 非标准但普遍支持, 记录导致错误的嵌套调用序列, 供调试使用;- 构造:
new Error(message)、new SyntaxError(message)等; 内置错误的name就是构造器名; - 字符串化: 直接打印错误对象相当于
"name: message", 例如String(err)得"ReferenceError: lalala is not defined";
如何区分与抛出错误?
- 区分: 用
instanceof按类型分支处理;
try {
someFunction();
} catch (error) {
if (error instanceof TypeError) {
// 类型错误
} else if (error instanceof ReferenceError) {
// 引用错误
} else {
// 其它
}
}
- 抛出:
throw可抛任意值, 建议抛出内置错误类型的实例;
throw new TypeError("What type of variable do you take me for?");
什么时候应该把错误重新抛出?
- 事实:
catch接到的是try里的所有错误, 不只你预期的那种; - 风险: 把编程错误 (如变量未定义) 当成数据错误处理, 会打印误导信息并掩盖真正的 bug;
- 规则: 只处理自己认识的错误, 其余
throw err原样上抛; - 识别: 优先用
instanceof(对子类也成立); 拿不到类时比较err.name或err.constructor.name; - 后果: 从
catch抛出的错误会穿过本层try...catch, 交给外层处理或终止脚本;
try {
run();
} catch (err) {
if (err instanceof SyntaxError) console.log("JSON Error: " + err.message);
else throw err; // 不知道怎么处理, 往上抛
}
如何自定义与包装错误?
- 继承: 自定义错误类
extends Error, 这样instanceof Error依然成立; 还可继续向下派生形成层级; - 必做: 子类构造器先
super(message), 再把this.name改回类名 (父构造器会置为"Error"); - 附加字段: 子类可携带自己的信息, 如
HttpError的statusCode; - 减样板: 做个基类统一写
this.name = this.constructor.name, 子类就不必各自赋值;
class MyError extends Error {
constructor(message) {
super(message);
this.name = this.constructor.name;
}
}
class ValidationError extends MyError {}
new ValidationError("bad").name; // "ValidationError"
- 包装: 函数内部捕获多种低级错误, 改抛更抽象的
ReadError, 原始错误挂在cause; 调用方只需判断一种类型, 需要细节时再读cause, 不认识的错误照旧上抛;
class ReadError extends Error {
constructor(message, cause) {
super(message);
this.cause = cause;
this.name = "ReadError";
}
}
// readUser: try { JSON.parse(json); } catch (err) { throw new ReadError("Syntax Error", err); }