JS笔记之JS错误处理(一).md
JS笔记之JS错误处理(一)
在编程中很难能够一次性写出完美的代码,错误确实经常性的,在JS中可以使用try…catch能够使我们捕获错误。
1 try…catch语法
try…catch 结构由两部分组成:try 和 catch:
1 | try { |
他的执行步骤是:
- 首先执行try…的代码。
- 如果第一步没有错误,则忽略catch(err)…的代码,跳过catch…的代码,执行try的末尾继续执行。
- 如果第一步错误,则try…停止执行,代码则跳转带catch(err)的开头,变量err(可以是任何名称)将包含一个error对象,该对象包含了所发生事件的详细信息。
所以 try…catch(err)… try后面的代码不会导致程序停止。一些例子:
1 | // 没有 error 的例子:显示 alert (1) 和 (2): |
Tips:try…catch… 仅仅对运行时的error有效,也就是说要想try…catch能够工作,代码必须是可执行的,如果包含语法错误,那么try..catch将无法工作。
JS引擎首先会读取代码,然后运行它,在读取阶段发生的错误,被称为解析时间(parse-time)错误,并且无法修复,因为引擎无法理解代码。
所以try…catch只能处理有效代码中出现的错误,这列错误成为运行时的错误(runtime errors),也被称为异常。
如果在”计划的”代码中发生异常,例如在setTimeout中,则try…catch不会捕获到异常。
1 | try { |
因为 try…catch 包裹了计划要执行的函数,该函数本身要稍后才执行,这时引擎已经离开了try…catch 结构。
如果要捕获到计划的函数的异常,那么try…catch 必须在这个函数内。
1 | setTimeout(function () { |
2 Error对象
发生错误时JS会生成一个包含此error的详细信息的对象,然后将该对象作为参数传递给catch,对于所有的内建的 error对象有两个主要的属性: name、message。
name:error的名称,例如对于一个未定义的变量,名称是“ReferenceError”。
message: 关于error的详细文字描述。
还有其他的非标准的属性,在大多数环境下可用,其中被广泛使用和支持的是:
stack: 代表当前的调用栈,用于调试目的的一个字符串,其中包含有关导致 error 的嵌套调用序列的信息。
1 | try { |
可选的catch绑定
这是一个新的特性,如果我们不需要error的信息,catch也可以忽略他。
1 | try { |
3 try…catch的真实用例
当我们接受来自网络、服务器或是其他来源的json数据,我们会使用 JSON.parse(str)来讲字符串解析为JS对象。
但是如果json字符串无法解析,那么就会生成一个error,所以这个时候就需要try…catch来尝试解析这个json字符串,即使发生了错误我们也能主动的捕获。
1 | let json = "{ bad json }"; |
这里我们将错误输出了,我们可以做更多的事情,例如新发送一个网络请求,或者将日志发送到记录日志的设备。
4 输出自定义的错误
有时候我们可能知道这里容易出问题,那么如何输出我们自定义的错误呢?
使用 throw 操作符, 语法:throw <object error>
技术上我们可以将任何东西用作于error,甚至可以是一个原型类型数据,又或者是字符串或数字,但是最好使用对象,最好具有name和message属性的对象。 某种程度上和内建的Error对象保持兼容。
JS中有很多内建的标准 error 的构造器:Error,SyntaxError,ReferenceError,TypeError 等。我们也可以使用它们来创建 error 对象。例如:
1 | let error = new Error(message); |
内建的error对象, name属性刚刚好就是构造器的名字,message 就是构造器的参数。
例如我们使用内建的错误对象,构造一个我们自己的错误:
1 | let json = '{ "age": 30 }'; // 不完整的数据 |
5 再次抛出error
在上述的例子中 try…catch 用于捕获 数据不正确的error, 但是 try…代码块可能会继续抛出其他的错误。
1 | let json = '{ "age": 30 }'; // 不完整的数据 |
实际上,catch会捕获到所有来自于try…代码块的error,上面的代码中 错误已经不再是 json error 了, 是其他的错误,但是错误信息还是 JSON Error。
为了避免此类的问题,我们采用“重新抛出技术”,规则很简单:
catch 只处理他知道的error,并抛出其他的error。
再次抛出的技术可以理解为:
- catch 捕获所有的error。
- 在catch(err){…} 块中,我们对error对象err进行分析。
- 如果我们不知道如何处理它,那么我们就throw err。
通常我们可以使用instanceif 操作符判断错误类型:
1 | try { |
我们还可以从err.name 属性中获取错误类型的类名,所有的原生的错误都有这个类型,另一种方式是读取err.constructor.name。
1 | let json = '{ "age": 30 }'; // 不完整的数据 |
6 try…catch…finally
try…catch 还有一个代码结构, finally的代码所有的情况都会被执行,
1 | try { |
一个例子:
1 | let num = +prompt("输入一个正整数?", 35) |
Tips: 变量和 try…catch…finally 中的局部变量
请注意,上面代码中的 result 和 diff 变量都是在 try…catch 之前 声明的。
否则,如果我们使用 let 在 try 块中声明变量,那么该变量将只在 try 块中可见。
1 | function func() { |
没有catch…finally 结构也很有用,当我们不想在这处理error,但是需要确保我们启动的处理需要被完成。
7 全局的catch
在浏览器中我们可以讲一个函数赋值给特殊的 window.onerror 属性,该函数在发生未捕获的error时执行。
语法如下:
1 | window.onerror = function (message, url, line, col, error) { |
例如:
1 | <script> |
全局错误处理程序的作用通常不是回复程序的方法,而是在程序发生错误的时候,将错误消息发送给开发者。
8 总结
try…catch 结构允许我们处理执行过程中出现的error, 从字面上拉看他允许尝试运行代码,并捕获其中可能发生的error。
error 对象包括以下属性:
- name error名称的字符串。
- message 人类可读的错误信息。
- stack error发生时的调用栈。
如果需要error对象,也可以直接使用 try{…}catch {…}。
我们可以使用throw 抛出自己根据内建对象定义的错误对象。
再次抛出 throwing 是一种错误处理的常用模式。