1 Error及其派生对象
1.1 Error对象
JavaScript在解析或运行时,一旦发生错误,引擎就会抛出一个错误对象。JavaScript原生提供了Error构造函数,所抛出的错误都是Error构造函数的实例。
var err=new Error('出错了');
err.message //"出错了"
以上示例中,参数为message
属性,这个参数是必须的。除此之外,还提供了name
(错误名称)属性和stack
(错误的堆栈)属性,但这两个属性是非标准的,不是每种实现都有。
1.2 Error派生对象
Error实例对象是最一般的错误类型,在此基础上JavaScript还定义了6种错误类型:
-
SyntaxError
:解析代码时的语法错误。 -
ReferenceError
:引用一个不存在的变量时发生的错误。 -
RangeError
:一个值超出有效范围时发生的错误。 -
TypeError
:变量或参数不是预期类型时发生的错误。 -
URIError
:URI 相关函数的参数不正确时抛出的错误。主要涉及encodeURI()
、decodeURI()
、encodeURIComponent()
、decodeURIComponent()
、escape()
和unescape()
这六个函数。 -
EvalError
:eval
函数没有被正确执行时抛出的错误。
针对以上6种派生对象,我们都可以通过new
关键字手动生成错误对象的实例。同样地,message
也是必要参数。
2 自定义错误
除了以上的原生提供的错误对象,我们还可以自定义错误对象。代码实现如下:
function MyError(message){
this.message="...";
this.name="...";
}
MyError.prototype=new Error();
MyError.prototype.constructor=MyError;
然后我们就可以通过new
关键字来新建自定错误对象了。
3 错误处理
-
throw
语句:手动中断程序执行,抛出一个错误(自定义错误也可以)。 -
try...catch
结构:try
代码块中的错误会被catch
代码块捕获处理。如果不确定某些代码是否会报错,就可以把它们放在try...catch
代码块之中。举个例子:
注:
catch
代码块之中,还可以再抛出错误,甚至使用嵌套的try...catch
结构。try { throw "出错了"; } catch (e) { console.log(111); } console.log(222);
输出结果为:
111 222
分析:从运行结果看,程序没有中断执行,并且继续向下执行完毕。
另外为了捕获不同类型的错误,从而进行不同的处理,也可以在
catch
块中加入判断语句:try { //... } 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
结构允许在最后添加一个finally
代码块,表示不管是否出现错误,都必需在最后运行的语句。-
来看一个典型的应用场景:首先打开一个文件,然后在
try
代码块中写入文件,如果没有发生错误,则运行finally
代码块关闭文件;一旦发生错误,则先使用catch
代码块处理错误,再使用finally
代码块关闭文件。openFile(); try { writeFile(Data); } catch(e) { handleError(e); } finally { closeFile(); }
-
再举个例子:没有
catch
块,try
块中有抛出错误。function cleansUp() { try { throw new Error('出错了……'); console.log('此行不会执行'); } finally { console.log('完成清理工作'); } } cleansUp()
运行结果如下:显然,在中断执行前首先执行了
finally
块,然后再抛出的错误信息。完成清理工作 Uncaught Error: 出错了…… at cleansUp (<anonymous>:3:11) at <anonymous>:10:1
-
再来看一个例子:分析一下
return
语句与finally
块的执行顺序。var count = 0; function countUp() { try { return count; } finally { ++count; } } countUp() //0 count //1
分析:虽然
try
块中有return
语句,但还是运行了finally
代码块,而且return
语句执行是在finally
块之前的,只是等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
代码块结束执行之前,一遇到return
语句,就会先执行finally
代码块,由于return false
语句,因此就直接返回了,不再会回去执行catch
代码块剩下的部分了。 -
再举个例子:
catch
块中有throw
语句,如下例子所示:function f() { try { throw '出错了!'; } catch(e) { console.log('捕捉到内部错误'); throw e; // 这句原本会等到finally结束再执行 } finally { return false; // 直接返回 } } f()
运行结果为:
捕捉到内部错误
分析:进入
catch
代码块之后,一遇到throw
语句,就会去执行finally
代码块,其中有return false
语句,因此就直接返回了,不再会回去执行catch
代码块剩下的部分了。
总结:
throw
语句和return
语句都会触发finally
块的执行。 -