异常
本节课的重点是异常的分类
剩余两部分的知识,绝大部分情况下都用不到,除非你要写一些 高端 的代码
js
Uncaught TypeError: Cannot read properties of undefined (reading 'toString')
at <anonymous>:1:3异常并非坏事,它可以让开发人员及时发现错误、定位错误,甚至在某些时候,我们还需要故意的抛出异常
异常的分类
在JS中,异常表现为一个对象,不同的对象表达了不同的异常类型,不同类型的异常对应到不同的错误
| 异常类型 | 含义 |
|---|---|
| SyntaxError | 语法错误 |
| ReferenceError | 引用错误,往往是使用了未定义的变量或函数 |
| TypeError | 类型错误,往往是使用了一个对象中不存在的成员 |
每个异常都是一个对象,通过对应的构造函数创建
所有的异常构造器都继承自Error,更多信息参见MDN
当代码运行过程中出现错误时,JS会:
- 自动创建对应的异常对象,抛出错误
- 程序终止运行
- 控制台中会显示异常对象
每个异常对象都至少记录了两个关键信息:
- 错误消息描述:描述异常出现的原因
- 调用堆栈信息:描述异常出现的位置
捕获异常
捕获异常就是处理错误,当错误发生后,我们对错误进行相应的处理,让程序不至于终止
js
try{
// 代码1
}
catch(err){
// 代码2:当代码1出现异常后,会执行这里的代码,异常对象会传递给err
}
finally{
// 代码3:可省略。无论是否有异常,都会执行
}
// 无异常的执行顺序:代码1 --> 代码3
// 有异常的执行顺序:代码1 --> 出现异常,中断代码1的执行 --> 代码2 --> 代码3在绝大部分时候,我们都无须捕获异常,除非满足以下要求:
- 我们能够预知某段代码会出现异常
- 我们知道出现异常后要做什么
上面的条件任意一个不满足,都不应该处理异常
永远不能为了不报错而捕获异常!
下面是一段可能使用异常捕获的伪代码
js
try {
var heros = network.getHeros(); // 从网络获取王者荣耀英雄数据,得到英雄数组
createHTML(heros); // 将数组生成HTML
}
catch(err) {
// 出现网络故障,给用户显示一个提示框
showErrorDialog('网络故障,请检查您的网络是否连接正常。故障原因:' + err.message);
}手动抛出异常
不仅浏览器会自动给我们抛出异常,我们还可以手动的抛出异常
js
throw 异常对象; // 当代码运行到这里,会终止执行,抛出异常对象,效果和浏览器抛出的错误完全一样当编写函数时,如果满足下面三个条件,就可以选择抛出异常:
- 预知执行过程中可能会出现某种错误
- 浏览器不会抛出这个错误
- 该函数无法处理这个错误
下面展现了一个需要抛出异常的例子
js
/**
* 得到两个数字之和
* 若传递的不是数字,则会抛出TypeError
* @param {number} a 数字1
* @param {number} b 数字2
* @return {number} 两数之和
*/
function sum(a, b){
if(typeof a !== 'number' || typeof b !== 'number'){
throw new TypeError('必须传入两个数字才能求和')
}
return a + b;
}规范:如果某个函数需要抛出异常,一定要在函数的文档注释中阐述清楚
断点调试
代码出现错误,在简单的场景下,一般使用console.log就可以定位问题并且解决。 如果遇到代码比较复杂的情况:我们可以使用断点调试。
断掉调试一定可以解决很多问题,属于最终的秘密武器。
一些经验:
- 遇到问题,大致定位到问题出现的地方,打上断点。
- 根据断点给出的信息,观察变量的值,找出问题或者流程所出现的问题。
- 如果遇到复杂的代码,可以先熟悉下别人写的代码,粗略的读取一下,知道代码的变量和功能,对整体有个把控。
- 从一个点进入,仔细的思考找到串联的点去找出问题,然后思考解决方案。
- 这是一种能力,需要日积月累的培养。