本文已参与「新人创作礼」活动,一起开启掘金创作之路。
错误的处理
在实际开发中我们会自己封装一些工具函数,并给其它人使用:
- 其它人在使用的时候,可能会传递一些参数;
- 对于函数来说,需要对这些参数进行验证,否则可能得到意外的结果;
很多时候,当我们验证到传入参数并不符合要求时,就会直接return;
但是return存在很大的弊端:
- 调用者并不清楚函数到底有没有正确执行(可能函数的执行结果就是一个undefined);
- 事实上,正确的做法应该是没有通过某些验证时,应该让外界知道函数内部报错了;
如果让一个函数告知外界自己内部出现错误了呢?
- 通过
throw语句,抛出一个异常;
throw语句:
- throw语句用于抛出一个用户自定义的异常;
- 当执行到throw语句时,当前函数就会终止执行(throw后面的语句不会被执行);
如果我们执行代码的之后遇到报错,拿到错误信息时我们就可以及时地去修正代码;
// 本意是对两个数字进行求和
function numberSum(num1, num2) {
return num1 + num2
}
numberSum('aaa', 'bbb')
// 在调用的时候传入参数类型变成了字符串,
// 导致函数的执行结果变成了字符串
function numberSum2(num1, num2) {
if (typeof num1 === 'number' && typeof num2 === 'number') {
return num1 + num2
} else {
throw 'arguments expect number type'
}
}
numberSum2('aaa', 'bbb')
执行结果:
throw关键字
throw表达式就是在throw后面可以跟上一个表达式来表示具体的异常信息:throw expression;
throw关键字可以跟上哪些类型呢?
- 基本数据类型:比如number,string,boolean;
- 对象类型:对象类型可以包含更多的信息;
function foo(type) {
if (type === 0) {
// throw 111
// throw 'type error'
// throw false
throw {
errCode: 1001,
errMessage: 'type error'
}
}
console.log('foo函数正常执行');
}
foo(0)
运行结果:
每次在抛出异常的时候通过字面量的方法写一个对象有点麻烦,所以我们可以创建一个错误类:
class MyError {
constructor(code, message) {
this.code = code
this.message = message
}
}
function foo(type) {
if (type === 0) {
// throw 111
// throw 'type error'
// throw false
throw new MyError(1001, 'type error')
}
console.log('foo函数正常执行');
}
foo(0)
运行结果:
Error类型
事实上,JavaScript已经给我们提供了一个Error类,我们可以直接创建这个类的实例对象:
function foo(type) {
if (type === 0) {
throw new Error('type error')
}
console.log('foo正常执行');
}
foo(0)
运行结果:
Error包含三个属性:
- messsage:创建Error对象时传入的message;
- name:Error的名称,通常和类的名称一致;
- stack:整个Error的错误信息,包括函数的调用栈,当我们直接打印Error对象时,打印的就是stack;
Error类还有一些自己的子类:
- RangeError:下标值越界时使用的错误类型;
- SyntaxError:解析语法错误时使用的错误类型;
- TypeError:出现类型错误时,使用的错误类型;
异常的处理
我们会发现在之前的代码中,一个函数抛出了异常,调用它的时候程序会被强制终止:
- 这是因为如果我们在调用一个函数时,这个函数抛出了异常,但是我们并没有对这个异常进行处理,那么这个异常会继续传 递到上一个函数调用中;
- 而如果到了最顶层(全局)的代码中依然没有对这个异常的处理代码,这个时候就会报错并且终止程序的运行 ;
比如我们下面这段代码:
function bar() {
throw Error('bar error')
}
function foo() {
bar()
}
function baz() {
foo()
}
baz()
console.log('后续代码执行');
它的运行结果是:
我们可以看到:
- 在bar函数执行的时候会抛出异常,并且在bar函数内部并没有对这个异常进行处理;
- 那么这个异常会传递到上一个函数调用中,也就是foo函数;
- 而foo函数同样没有对异常进行处理,于是又传递给了上一层函数baz;
- baz函数中也没有对异常进行处理,又继续传递到了全局代码中;
- 此时依旧没有被处理,这时候程序就会终止执行,后续代码都不会执行了;
异常的捕获
很多情况下出现异常时,我们并不希望程序直接就退出执行,而是希望可以正确处理异常;
这种情况下我们可以使用try catch;
function bar() {
throw new Error('bar error')
}
try {
bar()
}catch(e) {
console.log('捕获到了异常:', e);
}
console.log('后续代码的执行');
运行结果:
- 在ES10(ES2019)中,catch后面绑定的error可以省略(当然使用这种方式我们就无法获取到具体的异常了);
- 如果有一些必须要执行的代码,我们可以使用finally来执行:
-
- finally表示最终一定会被执行的代码结构;
- 注意:如果try和finally中都有返回值,那么会使用finally当中的返回值 ;
function foo() {
try {
return 'try return value'
} catch(e) {
console.log('foo error:', e);
} finally {
return 'finally return value'
}
}
console.log(foo()); // finally return value