Skip to content

异常

本节课的重点是异常的分类

剩余两部分的知识,绝大部分情况下都用不到,除非你要写一些 高端 的代码

js
 Uncaught TypeError: Cannot read properties of undefined (reading 'toString')
 at <anonymous>:1:3

异常并非坏事,它可以让开发人员及时发现错误、定位错误,甚至在某些时候,我们还需要故意的抛出异常

异常的分类

在JS中,异常表现为一个对象,不同的对象表达了不同的异常类型,不同类型的异常对应到不同的错误

异常类型含义
SyntaxError语法错误
ReferenceError引用错误,往往是使用了未定义的变量或函数
TypeError类型错误,往往是使用了一个对象中不存在的成员

每个异常都是一个对象,通过对应的构造函数创建

所有的异常构造器都继承自Error,更多信息参见MDN

当代码运行过程中出现错误时,JS会:

  1. 自动创建对应的异常对象,抛出错误
  2. 程序终止运行
  3. 控制台中会显示异常对象

每个异常对象都至少记录了两个关键信息

  1. 错误消息描述:描述异常出现的原因
  2. 调用堆栈信息:描述异常出现的位置

捕获异常

捕获异常就是处理错误,当错误发生后,我们对错误进行相应的处理,让程序不至于终止

js
try{
  // 代码1
}
catch(err){
  // 代码2:当代码1出现异常后,会执行这里的代码,异常对象会传递给err
}
finally{
  // 代码3:可省略。无论是否有异常,都会执行
}

// 无异常的执行顺序:代码1 --> 代码3
// 有异常的执行顺序:代码1 --> 出现异常,中断代码1的执行 --> 代码2 --> 代码3

在绝大部分时候,我们都无须捕获异常,除非满足以下要求:

  1. 我们能够预知某段代码会出现异常
  2. 我们知道出现异常后要做什么

上面的条件任意一个不满足,都不应该处理异常

永远不能为了不报错而捕获异常!

下面是一段可能使用异常捕获的伪代码

js
try {
  var heros = network.getHeros(); // 从网络获取王者荣耀英雄数据,得到英雄数组
  createHTML(heros); // 将数组生成HTML
}
catch(err) {
  // 出现网络故障,给用户显示一个提示框
  showErrorDialog('网络故障,请检查您的网络是否连接正常。故障原因:' + err.message);
}

手动抛出异常

不仅浏览器会自动给我们抛出异常,我们还可以手动的抛出异常

js
throw 异常对象; // 当代码运行到这里,会终止执行,抛出异常对象,效果和浏览器抛出的错误完全一样

当编写函数时,如果满足下面三个条件,就可以选择抛出异常:

  1. 预知执行过程中可能会出现某种错误
  2. 浏览器不会抛出这个错误
  3. 该函数无法处理这个错误

下面展现了一个需要抛出异常的例子

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就可以定位问题并且解决。 如果遇到代码比较复杂的情况:我们可以使用断点调试。

断掉调试一定可以解决很多问题,属于最终的秘密武器。

一些经验:

  • 遇到问题,大致定位到问题出现的地方,打上断点。
  • 根据断点给出的信息,观察变量的值,找出问题或者流程所出现的问题。
  • 如果遇到复杂的代码,可以先熟悉下别人写的代码,粗略的读取一下,知道代码的变量和功能,对整体有个把控。
  • 从一个点进入,仔细的思考找到串联的点去找出问题,然后思考解决方案。
  • 这是一种能力,需要日积月累的培养。

MIT License