错误处理机制
一、Error实例对象
- JavaScript 解析或运行时,一旦发生错误,引擎就会抛出一个错误对象。JavaScript 原生提供Error构造函数,所有抛出的错误都是这个构造函数的实例。
-
Error
实例对象必须有message
属性,表示出错时的提示信息。Error
实例还提供name
和stack
属性,分别表示错误的名称和错误的堆栈。但它们是非标准的,不是每种实现都有。
message
:错误提示信息
name
:错误名称(非标准属性)
stack
:错误的堆栈(非标准属性)
Error的六个派生对象:
SyntaxError
对象(语法错误)
SyntaxError
对象是解析代码时发生的语法错误。ReferenceError
对象(引用错误)
ReferenceError
对象是引用一个不存在的变量时发生的错误。
例如:将一个值分配给无法分配的对象,比如对函数的运行结果或者this
赋值。RangeError
对象(引用错误)
RangeError
对象是一个值超出有效范围时发生的错误
主要有几种情况,一是数组长度为负数,二是Number
对象的方法参数超出范围,以及函数堆栈超过最大值。TypeError
对象(类型错误)
TypeError
对象是变量或参数不是预期类型时发生的错误
例如: 对字符串、布尔值、数值等原始类型的值使用new命令URIError
对象(编码错误)
URIError
对象是URI
相关函数的参数不正确时抛出的错误EvalError
对象(全局错误)
eval函数没有被正确执行时,会抛出EvalError错误。该错误类型已经不再使用了,只是为了保证与以前代码兼容,才继续保留。
以上这6种派生错误,连同原始的Error对象,都是构造函数。开发者可以使用它们,手动生成错误对象的实例。这些构造函数都接受一个参数,代表错误提示信息(message)。
二、throw 语句
throw
语句的作用是手动中断程序执行,抛出一个错误。
if (x <= 0) {
throw new Error('x 必须为正数');
}
// Uncaught ReferenceError: x is not defined
throw
抛出的错误就是它的参数,这里是一个Error实例。
throw
可以抛出任何类型的值。也就是说,它的参数可以是任何值。
// 抛出一个字符串
throw 'Error!';
// Uncaught Error!
// 抛出一个数值
throw 42;
// Uncaught 42
// 抛出一个布尔值
throw true;
// Uncaught true
// 抛出一个对象
throw {
toString: function () {
return 'Error!';
}
};
// Uncaught {toString: ƒ}
对于 JavaScript 引擎来说,遇到throw语句,程序就中止了
三、try...catch结构
- 一旦发生错误,程序就中止执行了。JavaScript 提供了
try...catch
结构,允许对错误进行处理,选择是否往下执行。 - try语句包含了由一个或者多个语句组成的try块, 和至少一个
catch
子句或者一个finally
子句的其中一个,或者两个兼有。 - 三种形式的
try
声明:
1.try...catch
try...finally
try...catch...finally
try {
throw new Error('出错了!');
} catch (e) {
console.log(e.name + ": " + e.message);
console.log(e.stack);
}
// Error: 出错了!
// at <anonymous>:3:9
// ...
-
catch
子句包含try
块中抛出异常时要执行的语句。即:想让try
语句中的内容成功, 如果没成功,想控制接下来发生的事情,这时可以在catch
语句中实现。 如果在try
块中有任何一个语句(或者从try
块中调用的函数)抛出异常,控制立即转向catch
子句。如果在try
块中没有异常抛出,会跳过catch
子句。 -
catch
代码块捕获错误之后,程序不会中断,会按照正常流程继续执行下去。 -
catch
代码块之中,还可以再抛出错误,甚至使用嵌套的try...catch
结构。
var n = 100;
try {
throw n;
} catch (e) {
if (e <= 50) {
// ...
} else {
throw e;
}
}
// Uncaught 100
-
catch
接受一个参数,表示try
代码块抛出的值。 -
catch
代码块之中可以加入判断语句if...else
。用于判断错误类型,进行不同的处理。
try {
foo.bar();
} catch (e) {
if (e instanceof EvalError) {
console.log(e.name + ": " + e.message);
} else if (e instanceof RangeError) {
console.log(e.name + ": " + e.message);
}
// ...
}
-
finally
子句在try
块和catch
块之后执行但是在下一个try
声明之前执行。无论是否有异常抛出或捕获它总是执行。
四、finally 代码块
-
ry...catch
结构允许在最后添加一个finally
代码块,表示不管是否出现错误,都必需在最后运行的语句。
function cleansUp() {
try {
throw new Error('出错了……');
console.log('此行不会执行');
} finally {
console.log('完成清理工作');
}
}
cleansUp()
// 完成清理工作
// Uncaught Error: 出错了……
// at cleansUp (<anonymous>:3:11)
// at <anonymous>:10:1
上面代码中,由于没有catch
语句块,一旦发生错误,代码就会中断执行。中断执行之前,会先执行finally
代码块,然后再向用户提示报错信息。
function idle(x) {
try {
console.log(x);
return 'result';
} finally {
console.log('FINALLY');
}
}
idle('hello')
// hello
// FINALLY
上面代码中,try
代码块没有发生错误,而且里面还包括return语句,但是finally
代码块依然会执行。而且,这个函数的返回值还是result
。
-
return
语句的执行是排在finally
代码之前,只是等finally
代码执行完毕后才返回。 - 下面是finally代码块用法的典型场景。
openFile();
try {
writeFile(Data);
} catch(e) {
handleError(e);
} finally {
closeFile();
}
上面代码首先打开一个文件,然后在try
代码块中写入文件,如果没有发生错误,则运行finally
代码块关闭文件;一旦发生错误,则先使用catch
代码块处理错误,再使用finally
代码块关闭文件。
-
try...catch...finally
这三者之间的执行顺序,例:
function f() {
try {
console.log(0);
throw 'bug';
} catch(e) {
console.log(1);
return true; // 这句原本会延迟到 finally 代码块结束再执行
console.log(2); // 不会运行
} finally {
console.log(3);
return false; // 这句会覆盖掉前面那句 return
console.log(4); // 不会运行
}
console.log(5); // 不会运行
}
var result = f();
// 0
// 1
// 3
result
// false
上面例子中catch
代码块结束执行之前,会先执行finally
代码块。