What是什么
try...catch
语句标记要尝试的语句块,并指定一个出现异常时抛出的响应。
Why为什么使用?
程序要健壮,必须要设计报错机制。try {…}
块内的错误不会杀死脚本 — 我们有机会在 catch
中处理它。
基本语法
try {
//可能会导致错误的代码
} catch (error) {
//在错误发生时怎么处理
}finally {
//即使报错始终执行
}
即使try..catch中有return,finally也会在返回前执行。
Error对象
发生错误时,JavaScript 生成一个包含有关其详细信息的对象。然后将该对象作为参数传递给 catch
:
我们将 catch 块仅仅用于显示信息,但是我们可以做更多的事儿:发送一个新的网络请求,向访问者建议一个替代方案,将有关错误的信息发送给记录日志的设备,……。所有这些都比代码“死掉”好得多。
对于所有内建的 error,error 对象具有两个主要属性:
name
Error 名称。例如,对于一个未定义的变量,名称是 "ReferenceError"
。
message
关于 error 的详细文字描述。
还有其他非标准的属性在大多数环境中可用。其中被最广泛使用和支持的是:
stack
当前的调用栈:用于调试目的的一个字符串,其中包含有关导致 error 的嵌套调用序列的信息。
try {
lalala; // error, variable is not defined!
} catch(err) {
alert(err.name); // ReferenceError
alert(err.message); // lalala is not defined
alert(err.stack); // ReferenceError: lalala is not defined at (...call stack)
// 也可以将一个 error 作为整体显示出来as a whole
// Error 信息被转换为像 "name: message" 这样的字符串
alert(err); // ReferenceError: lalala is not defined
}
How使用条件
只有错误可预知时才用try。所有不可预知的错误用try都是不负责任的写法。
try catch的使用,永远应该放在你的控制范围之内,而不应该防范未知的错误。也就是说你很清楚知道这里是有可能”出错“的,而且你很清楚知道什么前提下会出错,你就是要故意利用报错信息来区分错误,后续的程序会解决所有的出错,让程序继续执行。
如果让用户先发现你根本没预料到的错误,而不是你先发现错误,你是失职的。
大多数情况下,try catch适用于两种场合:
1、浏览器原罪的场合:也就是兼容性场合,因为浏览器兼容性不是程序员能改正的,所以只能try catch:由于不同浏览器的报错提示是不一样的,根据捕获的浏览器的报错提示判断用户的浏览器,然后做出对应的措施,这时候使用try catch是巧妙的办法,如果用if就比较笨拙,因为if通常只能反馈真或假,不能直接反馈浏览器的报错内容。
2、考虑如下代码。window.a.b是非法的,再跟2对比就没有意义,这样非法的条件,在try catch中仍可以继续运行下去。但在if中window.a.b已经报错,整个页面都会坏掉。如果希望用if写,那么必须先判断window.a是否是合法的,window.a是合法的前提下再判断window.a.b是不是合法的,如果也是合法的,再判断window.a.b是否不等于2,这样是不是很蠢?这时就体现出try catch的妙处了,程序不但知道window.a.b !== 2是假的,而且直接可以知道究竟哪一步就已经是假的。
再想象一下,有一个变量是json.a.b.c,其中的a/b/c都可能是存在的也可能是不存在的,全看具体情况,这时候你简单的写if (json.a.b.c === 2) {…}是不行的,因为json.a.b就可能已经是非法的,所以你如果用if,就要考虑a是不是非法的、a是合法前提下b是不是非法的,b是合法前提下c是不是非法的。但是json.a.b.c === 2在try中就可以直接写,也就是说,我不关心究竟a/b/c谁是非法的,我只关心json.a.b.c到底是不是等于2,不等于2或者任何一步出错,对我来讲没有区别,反正都是不等于2,我不关心哪步出错,而且程序不会坏掉。这是一种比较省心的写法。
另外注意,try catch不能做真假判断,只能做非法判断。也就是说:try {1 === 2},虽然1===2是假,但是是合法的,catch不会捕捉到错误,也不会告诉你1 === 2到底是真是假。所以,写在try里的应该是json.a.b.c而不是json.a.b.c === 2。究竟是不是等于2,是后面的事,是if干的事。简单说,try catch用于捕捉报错,当你不关心哪一步错误,只关心有没有错,就用try catch。
前端可以用在上传图片、使用别人的js
库报错、async await
同步调接口等地方适用。
JSON.parse,如果 **json
** 格式错误,JSON.parse
** 就会生成一个 error,因此脚本就会“死亡”。**
对应数据类型的错误,建议小伙伴们用解构赋值指定默认值、&&
和||
来规避,所以慎用try catch。
经典请求接口的场景
async getAjax() {
try {
let response = await fetch(url);
let data = await response.json();
console.log(data);
} catch(e) {
console.error("Oops, error", e);
}
}
await
后面可以跟 Promise 对象,表示等待 Promiseresolve()
才会继续向下执行,如果 Promise 被reject()
或抛出异常则会被外面的try...catch
捕获。
注意事项
try catch捕获不到异步错误
尝试对异步方法进行try catch
操作只能捕获当次事件循环内的异常,对callback执行时抛出的异常将无能为力。
try {
setTimeout(()=>{
const A = 1
A = 2
},0)
} catch (err) {
// 这里并不能捕获回调里面抛出的异常
console.log("-----catch error------")
console.log(err)
}
异步情况想捕获异常,建议在异步函数里包一层try catch
。
setTimeout(() => {
try {
const A = 1
A = 2
} catch (err) {
console.log(err)
}
}, 0)
2、要使得 try..catch
能工作,代码必须是可执行的。换句话说,它必须是有效的 JavaScript 代码。
如果代码包含语法错误,那么 try..catch
将无法正常工作,例如含有...
:
try {
...
} catch(e) {
alert("The engine can't understand this code, it's invalid");
}
所以,try..catch
只能处理有效代码中出现的错误。这类错误被称为“运行时的错误(runtime errors)”,有时被称为“异常(exceptions)”。
几条建议:
- 如果无法处理某个异常,那就不要捕获它。
- 如果捕获了一个异常,请不要胡乱处理它。
- 尽量在靠近异常被抛出的地方捕获异常。
- 在捕获异常的地方将它记录到日志中,除非您打算将它重新抛出。
- 按照您的异常处理必须多精细来构造您的方法。
- 需要用几种类型的异常就用几种,尤其是对于应用程序异常。
- 把低层次的异常封装成层次较高程序员较容易理解的异常。
- 尽量输出造成异常的完整数据
- 尽量捕获具有特定含义的异常:比如SqlException,而不是简单地捕获一个Exception。
参考文档
(完)