Error 对象
一旦代码解析或运行时发生错误,JavaScript引擎就会自动产生并抛出一个Error对象的实例,然后整个程序就中断在发生错误的地方。
Error 对象也可用于用户自定义的异常的基础对象。
Error 对象的实例有三个最基本的属性:
- name:错误名称
- message:错误提示信息
- stack:错误的堆栈(非标准属性,但是大多数平台支持)
利用name和message这两个属性,可以对发生什么错误有一个大概的了解。
错误对象处理
异常的抛出方式
异常可以自动抛出或手动抛出。
一旦代码解析或运行时发生错误,JavaScript引擎就会自动产生并抛出一个Error对象的实例,然后整个程序就中断在发生错误的地方。
开发者也可以在预感程序无法继续执行的时候主动抛出错误。
抛出一个错误
当代码中发生错误时,我们通常会抛出一个 Error 对象。Error 对象可以作为扩展和创建自定义错误类型的原型。
抛出错误时,你必须使用 throw 关键字。为了捕获抛出的错误,则必须使用 try catch 语句把可能出错的代码块包起来,catch 的时候可以接收一个参数,该参数就是被抛出的错误。
在 try catch 语句之后有 finally,不论前面代码是否抛出错误 finally 里面的代码都会执行,这种语言的常见用途有:在 finally 中做些清理的工作。
ts
try {
throw new Error("Whoops!");
} catch (e) {
console.error(e.name + ": " + e.message);
}
捕获异常
try catch 只能处理同步代码,异步产生的错误不能用 try catch 捕获,而要使用回调捕获。原因在于 try catch 的作用仅仅是捕获当前调用栈的错误。
错误类型
javascript规范中总共有8中错误类型构造函数
- Error -- 错误对象
- SyntaxError --解析过程语法错误
- TypeError -- 不属于有效类型
- ReferenceError -- 无效引用
- RangeError -- 数值超出有效范围
- URIError -- 解析URI编码出错
- EvalError -- 调用eval函数错误
- InternalError -- Javascript引擎内部错误的异常抛出, "递归太多"
处理特定错误
可以通过判断异常的类型来特定处理某一类的异常,即判断 constructor 属性,当使用现代 JavaScript 引擎时,可使用 instanceof 关键字
ts
try {
foo.bar();
} catch (e) {
if (e instanceof EvalError) {
console.error(e.name + ": " + e.message);
} else if (e instanceof RangeError) {
console.error(e.name + ": " + e.message);
}
// ... etc
else {
// If none of our cases matched leave the Error unhandled
throw e;
}
}
抛出一个非Error对象的值
同样需要注意的是,你可以抛出不是 Error 对象的任意值。如果恰巧你需要处理错误的调用栈信息和其他有意义的元数据,抛出一个非 Error 对象的错误没有任何帮助。
ts
function runWithoutThrowing(func) {
try {
func();
} catch (e) {
console.log('There was an error, but I will not throw it.');
console.log('The error message was: ', e, e.message)
}
}
function funcThatThrowsString() {
throw 'I am a String.';
}
runWithoutThrowing(funcThatThrowsString);
比如上面的代码,catch e 捕获到的值是一个字符串,没有message和stack等有用信息。
错误堆栈
调用栈
当有一个函数被调用的时候, 它就被压入到堆栈的顶部, 该函数运行完成之后, 又会从堆栈的顶部被移除。堆栈的数据结构就是后进先出。
些 JS 运行环境还提供了标准属性之外的属性,如 Node.js、Firefox、Chrome、Edge、IE 10、Opera 和 Safari 6+ 中会有 stack 属性,它包含了错误代码的调用栈,接下来我们简称错误堆栈。错误堆栈包含了产生该错误时完整的调用栈信息。
ts
function c() {
try {
var bar = baz;
throw new Error();
} catch (e) {
console.trace();
console.error(e);
console.log(e.stack);
}
}
function b() {
c();
}
function a() {
b();
}
a();
比如上面的例子,通过打印e.stack,可以看到错误发生时的调用栈。
异常的传播
异常的传播是作用在函数调用栈。如果一个异常没有被catch,他会沿着调用栈层层传递,直到栈为空。当一个异常被 throw 出来的时候,传播就开始了。如果一个异常没有被 catch ,最终会打印在控制台中。错误类型是 UncaughtError。
使用promise进行错误处理
promise 链在错误(error)处理中十分强大。当一个 promise 被 reject 时,控制权将移交至最近的 rejection 处理程序。
比如下面的例子,.catch
不必是立即的。它可能在一个或多个 .then
之后出现。
链尾端的 .catch 的表现有点像 try..catch。我们可能有许多个 .then 处理程序,然后在尾端使用一个 .catch 处理上面的所有 error。
ts
fetch("http://www.test.com")
.then((response) => {
console.log("接受到response");
return response.json();
})
.catch((err) => console.log("错误", err));
隐式try...catch
promise 的执行者(executor)和 promise 的处理程序周围有一个"隐式的 try..catch"。如果发生异常,它就会被捕获,并被视为 rejection 进行处理。
ts
new Promise((resolve, reject) => {
throw new Error("执行错误"); // 执行处抛出错误
}).catch((err) => {
console.log("捕获到错误", err);
});
ts
new Promise((resolve, reject) => {
resolve("正确执行");
})
.then((res) => {
throw new Error("发生错误", res);
})
.catch((err) => {
console.log("捕获到错误", err);
});
但对于异步抛出的错误,这里是无法捕获的。但通过reject返回的错误可以被捕获。
ts
new Promise((resolve, reject) => {
setTimeout(() => {
throw new Error('发生错误')
}, 1000)
}).catch((err) => {
console.log("捕获到错误", err);
});
ts
new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error('发生错误'))
}, 1000)
}).catch((err) => {
console.log("捕获到错误", err);
});
catch 中处理错误
我们通常在catch中处理错误,catch语句会返回一个promise。
如果catch中处理了错误,那么会继续调用后续的执行程序。
如果catch中无法处理错误,可以将错误抛出,将控制权交给下一个最近的错误处理程序。
ts
new Promise((resolve, reject) => {
reject(new Error("发生错误"));
})
.catch((err) => {
if (err instanceof URIError) {
// 处理已知错误
} else {
// 未知错误,再次抛出
throw err;
}
})
.then((res) => {
console.log("then...", res);
})
.catch((e) => {
console.log("捕获到错误", e);
});
unhandledrejection
当 Promise 被 reject 且没有 reject 处理器的时候,会触发 unhandledrejection 事件。
换句话说:当 Promise 的状态变为 rejection 时,我们没有正确处理,让其一直冒泡(propagation),直至被进程捕获。这个 Promise 就被称为 unhandled promise rejection。
unhandledrejection 继承自 PromiseRejectionEvent,而 PromiseRejectionEvent 又继承自 Event。因此unhandledrejection 含有 PromiseRejectionEvent 和 Event 的属性和方法。主要包含两个有用的属性:
- promise:特定的被 reject 且没有 reject 处理器的 Promise
- reason:将会传入异常处理程序中的错误原因(如果存在)
ts
new Promise((resolve, reject) => {
reject(new Error("发生错误"));
})
window.addEventListener("unhandledrejection", (event) => {
console.warn('UNHANDLED PROMISE REJECTION:', event.reason);
});
上面的例子中,触发了unhandledrejection 事件,未捕获的错误。