理解异常在javaScript面向对象编程是非常重要的,异常是一种非常强大的处理错误的方式。
错误处理
首先我们来看一个有问题的代码:
nonexistant();
在以上这个样例中,訪问一个不存在的变量,在这样的情况下,程序会怎么处理?非常早曾经的处理方式就是程序直接崩溃死掉,所以我们不能容忍这样的处理方式,须要有办法来处理。
最简单的处理方式是先检查,像这样:
if (window.func) {
func();
}
上面这样的处理方式,仍然可能还会出现错误。
由于window.func可能不是一个函数。因此我们仍须要这样检查:
if (typeof(func) == 'function') {
func();
}
在上面的样例中,typeof确保变量存在并确保是个函数。
我们满怀希望地做了非常多须要的检查来确保运行func是安全的。可是假设func的函数体内有错误呢?我们更想做的是处理错误,而不是让程序死掉。
我们能够用trycatch结构来处理。
Try catch 结构
用try…catch来取代我们经常使用的if语句结构来处理错误,我们用例如以下代码来改写上面的样例:
try {
func()
} catch(e) {
alert(e)
}
假设在try块中出现错误,这个时候catch块会起作用,參数e被赋值为一个特别的异常对象。该对象包含异常发生时的一些信息。
变量e是Error对象的一个实例(或者从TypeError,ReferenceError等继承)。
这个错误的属性在不同浏览器有点不一样。详情參考Error in MDN 和 Error inMSDN。
可是基本属性是同样的:
name:错误类型,对于浏览器产生的error会匹配error构造函数如TypeError, ReferenceError 等。
message: 告诉我们更具体的error信息。
在下面样例中,我们在try块中添加其它声明,name和message会被打印出来。
try {
var a = 5
var res = func(a)
if (res > 0) doA()
else doB()
} catch(e) {
alert("name:" + e.name + "\nmessage:" + e.message)
}
非常多异常都能够被try..catch捕获,你仅需通过try来检測可能的错误即可了。
获取栈的信息
Firefox,Chrome, Opera浏览器提供了stack属性,通过stack属性我们能够看到全部导致异常的嵌套的调用信息,样例例如以下:
function f(a) {
g(a+1)
}
function g(a) {
notexists;
}
try { f(1) } catch(e) { alert(e.stack)
不幸的是在IE中没有这个属性。甚至在IE9中也没有。
Try…catch…finally完整形式
完整的组成形式是由3部分组成:
try {
.. try statemenets ..
} catch(exception) {
.. catch statements ..
} finally {
.. finally statements ..
运行步骤例如以下:
1. Try中的声明会被运行。假设没有发生错误,catch部分会被忽视。
2. 假设发生错误,exception变量会被赋值为错误对象。而且catch中的声明也会被运行。
3. 在以上两种情况下,在try或者catch中无论有没有运行,finally代码都会被运行。
try…catch…finally…return
在下面样例中,假设try中有return语句而且有finally块时,finally中的运行完后,运行return语句。
function inc(a) {
try {
return a+1
} catch(e) {
// ..
} finally {
alert('done')
}
}
alert( inc(1) )
//运行结果:‘done’, 2
Throw声明
全部的错误能够被分成两种:
1. 程序设计错误:通常是由开发者造成的。如输入错误。
2. 异常流错误:这个错误是程序运行过程中的正常部分。一个常见的错误是表单验证。
假设用户输入了一些错误,正常的做法是处理这个错误而且叫用户反复输入。
用try…catch来处理异常错误,须要通过throw手动抛出错误。
语法是:throw e,e能够是不论什么东西。无论你抛出什么,都能够被catch…捕获,可是假设在try块外面抛出程序可能会崩溃。
下面的样例展示了throw是怎样工作的。
try {
throw 5
} catch(e) {
alert("Caught: "+e)
}
表单验证样例
比如,我们对年龄进行验证,检查是否合法。
function validateAge(age) { // age is a text to check
if (age === '') return; // no age is valid
age = +age
if (isNaN(age)) {
throw { name: 'BadAge', message: 'Invalid age' }
}
if (age < 5 || age > 150) {
throw { name: 'BadAge', message: 'Age out of range' }
}
}
try {
var age = prompt("Enter your age please?")
validateAge(age)
alert("The age is accepted!")
} catch(e) {
alert("Error: "+e.message)
}
经常来说。抛出的异常对象最好是从Error对象继承。提供一种更好的方式来管理error在上面的样例中应该这样实现:throw new BadAgeError("Invalid age")。
验证变化
如今假设须要加入一个验证条件,验证用户所提供的值是必须提供的。而且是有效的年龄。
比方我们实现了validateAge和validateRequired。
错误的检測方法
var value = input.value
// VALIDATE
var error = validateRequired(value)
if (!error) {
error = validateAge(value)
}
if (!error) {
/* another validator... */
}
// FINISHED VALIDATING
if (error) {
/* process error */
} else {
/* success */
}
Try…catch方法
这样的方式就是当检測到错误时手动抛出。
var value = input.value
try {
validateRequired(value)
validateAge(value)
// other validators in a row
/* success */
} catch(e) {
/* process error */
}
我们不须要一个一个地来检測,仅仅须要把可能出现错误的验证放到try块中即可了。假设一有错误。在catch中就会捕获到。没有错误自然非常顺利地运行。不会进入到catch块中。
比較
用try..catch处理错误有一些长处和缺点:
1. Try…catch方法处理错误更干净、简单可依赖,能够捕获全部错误。
2. 有可能存在一些不能检測的异常,try…catch是唯一能解决办法。
比如检測浏览器的一些特性。
3. Try…catch结构会占领几行代码的位置,看起来代码不太优雅。
异常分析和又一次抛出
有时代码会产生不同的错误,这样的情况下,经经常使用if来选择正确的处理方式。下面是伪代码。
try {
// 1. do smth
} catch(e) {
if (e instanceof ValidationError) {
// 2.1 process e
} else if (e instanceof PermissionError) {
// 2.2 process e
} else {
// 3. we don't know how to deal with e
throw e
}
}
1. 在try块中的代码比較复杂,或许会抛出异常。有些异常我们知道怎么处理,比方ValidationError,可是其它一些异常不知道。
2. 在catch块中,我们分析异常而且处理它。
3. 否则。异常又一次抛出,假定在外面另一层try…catch块知道怎么处理该异常。
异常要么被又一次抛出,要么被处理。千万不要无论它。除非你全然知道你在做什么。
try {
func()
} catch(e) {
if (e instanceof KnownError) {
// ...
}
}
在上面代码片段中。除了KnownError异常外,其它异常都被忽视了。
坦白地说。在java的世界里有这样的不处理异常的情况存在,可是留下不处理的异常总有隐患。
想象下,假设在func中代码有输入错误,这将会非常难调试。由于TypeError 和ReferenceError 异常被忽视了。
总结
1. Try…catch…finally结构同意你在try块中加入几种声明,能够在各自的catch块中进行异常处理。
2. 同意处理全部错误。包含JS自己产生的和手动抛出的。
3. 严格来说javaScript同意throw不论什么值,可是推荐全部的错误继承Error对象,形成继承层级。在这样的情况下,instanceof 工作得非常好。比如你能够捕获全部e instanceof ValidationError的异常。ValidationError包含AgeValidationError, RequiredValidationError 等。