JavaScripttry-catch语句(错误处理)
错误处理在处理程序设计中的重要性是⽏庸置疑的,任何有影响⼒的web应⽤程序都需要⼀套完善的错误处理机制。当然,⼤多数佼佼者确实做到了这⼀点,但通常只有服务器端应⽤程序才能做到如此。实际上,服务器端团队往往会在错误处理机制上投⼊较⼤精⼒,通常要考虑按照类型、频率,或者其他重要的标准对错误进⾏分类。这样⼀来,开发⼈员就能够理解⽤户在使⽤简单数据库查询或者报告⽣成脚本时,应⽤程序可能会出现的问题。
虽然客户端应⽤程序的错误处理也同样重要,但真正受到重视,还是最近⼏年的事。实际上,我们要⾯对这样⼀个不争的事实:使⽤web的绝⼤多数⼈都不是技术⾼⼿,其中甚⾄很多⼈根本就不明⽩浏览器到底是什么,更不⽤说让他们说喜欢哪⼀个了。每个浏览器在发⽣JavaScript错误时的⾏为都或多或少有⼀些差异。有的会显⽰⼩图标,有的则什么动静都没有,浏览器对JavaScript错误的这些默认⾏为对最终⽤户⽽⾔,毫⽆规律可循。最理想的情况下,⽤户遇到错误搞不清为什么,他们会再试着重做⼀次,最糟糕的情况下,⽤户会恼羞成怒,⼀去不复返了。良好的错误处理机制可以让⽤户及时得到提醒,知道到底发⽣了什么事,因⽽不会惊慌失措。为此,作为开发⼈员,我们必须理解在处理JavaScript错误的时候,都有哪些⼿段和⼯具可以利⽤。
⼀,try-catch 语句
ECMA-262第3版引⼊了try-catch语句,作为JavaScript中处理异常的⼀种标准⽅式。基本的语法如下所⽰,显⽽易见,这与Java中的try-catch语句是完全相同的:
try {
//可能会导致错误的代码
} catch (error) {
//在错误发⽣时怎么处理
}
也就是说,我们应该把所有可能会抛出错误的代码都放在try语句快中,⽽把那些⽤于错误处理的代码放在catch块中,例如:
try {
window.someNonexistentFunction(); //调⽤不存在的函数
} catch (error) {
alert('An error happened!');
}
如果try块中的任何代码发⽣了错误,就会⽴即退出代码执⾏过程,然后接着执⾏catch块,此时,catch块会接收到⼀个包含错误信息的对象。与在其他语⾔中不同的是,即使你不想使⽤这个错误对象,也要给它起个名字。这个对象中包含的实际信息会因浏览器⽽异,但共同的是有⼀个保存着错误信息的message属性。ECMA-262还规定了⼀个保存着错误类型的name属性,当前所有浏览器都⽀持这个属性(Opera9之前的版本不⽀持这个属性)。因此,在发⽣错误时,就可以像下⾯这样实事求是地显⽰浏览器给出的信息:
try {
window.someNonexistentFunction(); //调⽤不存在的函数
} catch (error) {
ssage);
}
这个例⼦在向⽤户显⽰错误信息时,使⽤了错误对象的message属性,这个message属性是唯⼀⼀个能保证所有浏览器都⽀持的属性,除此之外,IE、Firefox、Safari、Chrome以及Opera都为事件对象添加了其他相关信息。IE添加了与message属性完全相同的description属性,还添加了保存着内部错误数量的number属性。Firefox添加了fileName、lineNumber和stack(包含栈跟踪信息)属性。Safari添加了line(表⽰⾏号)、sourceId(表⽰内部错误代码)和sourceUrl属性。当然,在跨浏览器编程时,最好还是只使⽤message属性。
1,finally⼦句
虽然在try-catch语句中是可选的,但finally⼦句⼀经使⽤,其代码⽆论如何都会执⾏。换句话说,try语句块中的代码全部正常执⾏,finally⼦句会执⾏;如果因为出错⽽执⾏了catch语句块,finally⼦句照样还会执⾏。只要代码中包含finally⼦句,则⽆论try或catch语句块中包含什么样的代码——甚⾄return语句,都不会阻⽌finally⼦句的执⾏。来看下⾯这个函数:
function testFinally() {
try {
return 2;
} catch (error) {
return 1;
} finally {
return 0;
}
}
testFinally(); //0
这个函数在try-catch语句的每⼀部分都放了⼀条return语句。表⾯上看,调⽤这个函数会返回2,因为返回2个return语句位于try语句块中,⽽执⾏该语句⼜不会出错。可是,由于最后还有⼀个finally⼦句,结果就会导致该return语句被忽略,也就是说,调⽤这个函数只能返回0,如果把finally⼦句拿掉,这个函数将返回2.(请读者务必要记住,只要代码中包含finally⼦句,那么⽆论try还是catch语句块中的return语句都将被忽略。因此,在使⽤finally⼦句之前,⼀定要⾮常清楚你想要代码怎么样)
2,错误类型
执⾏代码期间可能会发⽣的错误有多种类型,每种错误都有对应的错误类型,⽽当错误发⽣时,就会抛出相应类型的错误对象。ECMA-262
定义了下列7种错误类型:
01, Error
02, EvalError
03, RangeError
04, ReferenceError
05, SyntaxError
06, TypeError
07, URIError
其中,Error是基类型,其他错误类型都继承⾃该类型,因此,所有错误类型共享了⼀组相同的属性(错误对象中的⽅法全是默认的对象⽅法)。Error类型的错误很少见,如果有也是浏览器抛出的;这个基类
型的主要⽬的是供开发⼈员抛出⾃定义错误。
EvalError类型的错误是在使⽤eval()函数⽽发⽣异常时抛出。ECMA-262中对这个错误有如下描述:“表⽰全局函数eval的使⽤⽅式与其定义不相符。“除此之外,并没有救到底什么情况会引发这种错误给出说明。在实际开发中碰到这种错误的可能性并不⼤。
RangeError类型的错误会在数值超出相应范围时触发。例如,在定义数组时,如果指定了数组不⽀持的项数(如-20或
Number.MAX_VALUE),就会触发这种错误。下⾯是具体的例⼦:
var items1 = new Array(-20); //VM77:1 Uncaught RangeError: Invalid array length(…)
var items2 = new Array(Number.MAX_VALUE); //VM79:1 Uncaught RangeError: Invalid array length(…)
JavaScript中经常会出现这种范围错误。
在不到对象的情况下,会发⽣ReferenceError(这种情况下,会直接导致⼈所共知的“object expected"浏览器错误)。通常,在访问不存在的变量时,就会发⽣这种错误,例如:syntaxerror是什么错误
var obj = x; //VM112:1 Uncaught ReferenceError: x is not defined(…) 在x未声明的情况下抛出ReferenceError
⾄于SyntaxError,当我们把语法错误的JavaScript字符串传⼊eval()函数时,就会导致此类错误,例如:
eval('a ++ b'); //VM114:1 Uncaught SyntaxError: Unexpected identifier(…)
如果语法错误的代码出现在eval()函数之外,则不太可能发⽣SyntaxError,因为此时的语法错误导致JavaScript代码⽴即停⽌执⾏。TypeError类型在JavaScript中经常⽤到,在变量中保存着意外的类型时,或者在访问不存在的⽅法时,都会导致这种错误。错误的原因虽然多种多样,但归根结底还是由于在执⾏特定于类型的操作时,变量的类型并不符合要求所致。下⾯来看⼏个例⼦:
最常发⽣类型错误的情况,就是传递给函数的参数事先未经检查,结果传⼊类型与预期类型不相符。
在使⽤encodeURI()或decodeURI(),⽽URI格式不正确时,就会导致URIError错误,这种错误也很少见,
因为前⾯说的这两个函数的容错性⾮常⾼。
利⽤不同的错误类型,可以熟悉更多有关异常的信息,从⽽有助于对错误作出恰当的处理,要想知道错误的类型,可以像下⾯这样在try-catch语句的catch语句中使⽤instanceof操作符:
try {
someFunction();
} catch (error) {
if (error instanceof TypeError) {
//处理类型错误
} else if (error instanceof ReferenceError) {
//处理引⽤错误
} else {
//处理其他类型的错误
}
}
在跨浏览器编程中,检查错误类型是确定处理⽅式的最简便途径,包含在message属性中的错误消息会因浏览器⽽异。
3,善⽤try-catch
当try-catch语句中发⽣错误时,浏览器会认为错误已经被处理了。因⽽不会通过前⾯讨论的机制记录或报告错误。对于那些不要求⽤户懂技术,也不需要⽤户理解错误的Web应⽤程序,这应该说是个理想的结果。不过,try-catch能够让我们实现⾃⼰的错误处理机制。
使⽤try-catch最适合处理那些我们⽆法控制的错误,假设你在使⽤⼀个⼤型的JavaScript库中的函数,该函数可能会有意⽆意地抛出⼀些错误。由于我们不能修改这个库的源代码,所以⼤可将对该函数的调⽤放在try-catch语句当中。万⼀有什么错误发⽣,也好恰当地处理它们。
在明明⽩⽩地知道⾃⼰的代码会发⽣错误时,再使⽤try-catch语句就不太合适了。例如,如果传递给函数的参数是字符串⽽⾮数值,就会造成函数出错,那么就应该先检查函数的类型,然后再决定如果去做。在这种情况下,不应该使⽤try-catch语句。
4,try-catch语句执⾏顺序
看下⾯的例⼦:
执⾏顺序为:⾸先执⾏try语句块中的代码,如果抛出异常,接着执⾏catch语句块中代码,如果没有异常,catch语句块中代码将会被忽略,但不管是否有异常,最后最会执⾏finally⼦句。try后⾯必须接着⼀个catch或者finally,也就是说JavaScript中的try-catch可以有3中组合形式。即try-catch、try-finally、try-catch-finally三种形式。
try-catch⼀般的应⽤场景⼤家都⽐较熟悉,下⾯来看⼏个嵌套的例⼦:
上⾯这个例⼦中,最外部的try语句块中嵌套了⼀个try-finally语句,内部的try语句中抛出了⼀个异常,但是内部没有catch语句块,所以会执⾏最近的⼀个catch语句块,但是在跳出外部try包含语句块之前,需要先执⾏内部的finally语句块中的代码,所以最后的结果如上图所⽰。再看⼀个例⼦:
这个例⼦中,内部嵌套的语句块中有catch语句,所以当内部try语句块中抛出异常时,会接着执⾏内部的catch语句块,然后执⾏finally⼦句。由于异常已经在内部处理完成,所以外部的catch语句块会被忽略,所以最终结果如上所⽰。再看⼀个例⼦:
这个例⼦在上⾯例⼦的基础上,内部的catch语句块中⼜抛出了⼀个异常,所以,在执⾏完相应语句后,会接着执⾏外部的catch语句,结果如上所⽰。
⼆,抛出错误
与try-catch语句相配的还有⼀个throw操作符,⽤于随时抛出⾃定义错误。抛出错误时,必须要给throw操作符指定⼀个值。这个值是什么类型,没有要求。下列代码都是有效的。
throw 12345;
throw 'Hello world!';
throw true;
throw { name: 'JavaScript'};
在遇到throw操作符时,代码会⽴即停⽌执⾏。仅当有try-catch语句捕获到被抛出的值时,代码才会继续执⾏。
通过使⽤某种内置错误类型,可以更真实地模拟浏览器错误。每种错误类型的构造函数接受⼀个参数,即实际的错误信息。下⾯是⼀个例⼦:
throw new Error('Something bad happened.');
这⾏代码抛出了⼀个通⽤错误,带有⼀条⾃定义错误信息。浏览器会像处理⾃⼰⽣成的错误⼀样,来处理这⾏代码抛出的错误。换句话说,浏览器会以常规⽅式报告这⼀错误,并且会显⽰这⾥的⾃定义错误类型。像下⾯使⽤其他错误类型,也可以模拟出类似的浏览器错误:
throw new SyntaxError("I don't like your syntax.");
throw new TypeError("what type of variable do you take me for?");
throw new RangeError("Sorry, you just don't have the range.");
throw new EvalError("That doesn't evaluate.");
throw new URIError("Uri, is that you?");
throw new ReferenceError("You didn't cite your references properly.");
在创建⾃定义错误消息时,最常⽤的错误类型是Error、RangeError、ReferenceError和TypeError。另外,利⽤原型链还可以通过继承Error 来创建⾃定义错误类型。此时,需要为新创建的错误类型指定name和message属性。来看⼀个例⼦:
浏览器对待继承⾃Error的⾃定义错误类型,就像对待其他错误类型⼀样。如果要捕获⾃⼰抛出的错误并且把它与浏览器错误区别对待的话,创建⾃定义错误是很有⽤的。(IE只有在抛出Error对象的时候才会显⽰⾃定义错误信息。对于其他类型,它都⽆⼀例外地显⽰"exception thrown and not caught"(抛出了异常,且未被捕获))。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。