JavaScript错误处理操作实例详解
本文实例讲述了JavaScript错误处理操作。分享给大家供大家参考,具体如下:
良好的错误处理机制可以让用户得到及时的提醒,所以让我们来看看JavaScript提供了哪些针对错误处理的工具和方法吧O(∩_∩)O~
1try-catch语句
ECMA-262第3版引入了try-catch语句,这时JavaScript处理异常的标准方式:
try{ //可能会导致错误的代码 }catch(error){ //错误处理 }
如果try块中的代码发生了错误,会立即执行catch块的代码。catch块有一个包含错误信息的对象,它有一个message属性,表示的是浏览器给出的错误消息:
try{ window.someNonexistenceFunction(); }catch(error){ console.log(error.message); }
message属性是所有的浏览器都支持的属性,所以在跨浏览器编程中,最好只使用这个属性。
1.1finally子句
finally子句是可选的,如果使用了finally子句,里面包含的代码是绝对会被执行的!甚至连return语句都无法阻止它被执行:
functiontestFinally(){ try{ return2; }catch(error){ return1; }finally{ return0; } } console.log(testFinally());//0
IE7及更早的版本有一个bug:除非有catch子句,否则finally中的代码永远不会被执行!IE8修复了这个bug。
注意:只要在代码中使用了finally子句,那么不管return放在try还是catch语句中,都会被忽略!
1.2错误类型
当错误发生时,会抛出相应类型的错误对象。ECMA-262定义了7种错误类型:
- Error
- EvalError
- RangeError
- ReferenceError
- SyntaxError
- TypeError
- URIError
1.2.1Error
Error是基类型,即其他的错误类型都是从Error继承来的。可以利用Error来自定义错误类型。
1.2.2EvalError
EvalError是在使用eval()函数发生异常时被抛出。怎么才算是异常呢?如果没有把eval()当作函数来使用,就会抛出EvalError:
neweval();//抛出EvalError eval=foo;//抛出EvalError
实际开发中很少这样使用eval()函数的(除非脑袋秀逗了O(∩_∩)O~),所以很少会遇到EvalError。
1.2.3RangeError
RangeError会在数值超出规定范围时被抛出。比如在定义数组时,指定了数组不支持的数组项数,就会抛出这个错误:
varitem1=newArray(-20);//抛出RangeError varitem1=newArray(Number.MAX_VALUE);//抛出RangeError
1.2.4ReferenceError
找不到对象时,会抛出ReferenceError(这就是浏览器的知名的“objectexpected”错误)。一般在访问不存在的变量时,会抛出这个错误:
varobj=x;//x还未被声明,所以抛出ReferenceError
1.2.5SyntaxError
如果把带着错误语法的字符串传入eval()函数时,就会抛出SyntaxError:
eval("a++b");//抛出SyntaxError
在eval()函数之外的语法错误,会导致JavaScript立即停止执行,所以不会抛出这个错误!
1.2.6TypeError
如果变量中保存着意外的类型,或者访问不存在的方式时,就会抛出TypeError。这个错误比较常见:
varo=new10;//抛出TypeError alert("name"intrue);//抛出TypeError Function.prototype.toString.call("name");//抛出TypeError
如果传递给函数的参数类型与预期类型不符,也会抛出这个错误。
1.2.7URIError
使用encodeURI()或者decodeURI()时,URI的格式不正确,就会抛出URIError错误。一般很少发生,因为这两个函数有着非常高的容错性O(∩_∩)O~
可以针对这些错误类型进行恰当的处理:
try{ someFunction(); }catch(error){ if(errorinstanceofTypeError){ //处理类型错误 } ... }
因为包含在message中的消息会因浏览器而异,所以在跨浏览器编程中,最好直接检查这些错误类型。
1.3合理使用try-catch
try-catch最适合用于那些我们无法控制的错误,比如使用了一个开源库中的函数,而我们无法修改源代码的情况。
而对于自定义的函数,验证函数参数类型是否合法的情况,就不应该使用try-catch,因为这个函数是我们可以修改的,在使用参数前先进行验证才是正途。
2抛出错误
throw操作符可以抛出自定义的错误,必须要指定一个值,这个值可以是任意类型。代码在遇到throw操作符时,会立即停止执行。只有在try-catch语句捕获到被抛出的值时,才会继续执行。
通过使用之前说过的内置错误类型,可以模拟浏览器错误。这些错误类型的构造函数都接收一个参数,即实际的错误消息:
thrownewError("Somethingbadhappened.");
最常使用这些错误类型来创建自定义的错误消息:Error、RangeError、ReferenceError和TypeError。
也可以利用原型链,通过继承Error来创建自定义消息。比如,这里为新创建的错误类型指定了name和message属性:
functionCustomError(message){ this.name="CustomError"; this.message=message; } CustomError.prototype=newError(); thrownewCustomError("Mymessage");
这样创建的自定义错误与浏览器错误并不同,所以可是很有用的哦O(∩_∩)O~
注意:IE只有在抛出Error对象时才会显示自定义的错误消息!其他错误对象,它只会显示“exceptionthrownandnotcaught”。
2.1抛出错误的时机
应该在已知的、某种特定错误条件下(会导致函数无法正常执行),抛出自定义的错误:
functionprocess(values){ if(!(valuesinstanceofArray)){ thrownewError("process():Argumentmustbeanarray."); } values.sort(); for(vari=0,len=values.length;i 100){ returnvalues[i]; } } return-1; } process("hi");
如果给上面的函数传入一个字符串参数,那么sort()就会抛错,所以我们在这里加入了带有适当消息的自定义错误。这对于一个有着几千行脚本代码的项目来说,可以显著地提升代码的可维护性O(∩_∩)O~
错误消息时包含了函数名称以及为什么会发生错误的明确描述,是不是很清晰呀O(∩_∩)O~
在开发时,要注意函数可能失败的因素,把这些因素都加上自定义错误吧!良好的错误处理机制是确保代码只发生我们定义的错误。
2.2抛出错误与使用try-catch
建议在抛出错误时尽量提供详尽的信息,因为我们抛出错误不就是为了在维护时,能够知道错误发生的具体位置和具体原因的吗?
3错误事件
任何没有通过try-catch处理的错误都会触发window对象的error事件(IE、Firfox和chrome支持)。它接收3个参数:错误消息、错误所在的URL和行号。一般只有错误消息有用。只能通过DOM0级技术来指定onerror事件处理程序,如果返回false,就可以阻止浏览器报告错误的默认行为:
window.onerror=function(message,url,line){ console.log(message); returnfalse; }
这个函数其实就是整个文档的try-catch语句,它可以捕获所有没有被处理的运行时错误。理想情况下,错误都被包含在try-catch语句中,所以不会使用到它。
注意:不同的浏览器下,这个函数的处理方式不同。在IE中,发生了onerror事件,代码仍会执行,所有的变量和数据都会保留。但在Firefox中,代码会停止执行,而且之前所有的变量和数据都会被销毁!
图像也支持error事件。如果图像的src属性中的URL无法返回被识别的图像格式时,就会触发持error事件。这时的error事件会返回一个以图像为目标的event对象:
varimage=newImage(); EventUtil.addHandler(image,"load",function(event){ console.log("Imageloaded!"); }); EventUtil.addHandler(image,"error",function(event){ console.log("Imagenotloaded!"); }); image.src="smilex.gif";//实际并不存在
注意,如果发生了error事件,那么这个图像的下载过程就已经结束咯。
4常见的错误类型
因为JavaScript是松散类型的语言,所以错误只会在代码运行期间发生。一般情况下,我们需要重点关注以下三种错误:
①类型转换错误
②数据类型错误
③通信错误
4.1类型转换错误
在使用相等(==)或不相等(!==),或者在if、for或while中使用了非布尔值时,最常发生类型转换错误。
所以建议使用全等(===)和不全等(!==)来避免类型转换操作。
if等语句会自动把任何值都转换为布尔值:
functionconcat(str1,str2,str3){ varresult=str1+str2; if(str3){//不要这样!!! result+=str3; } returnresult; }
这个函数是想拼接两个或三个字符串,然后返回结果。所以这里的第三个参数是可选的,,所以必须要检查。如果我们在调用这个函数时,只用到前两个参数,这意味着第三个参数因为是未使用过的命名变量,所以被自动赋值给undefine,而undefine在if中会被自动转换为false,这样可以。但是,如果第三个参数传入数值0,在if中会被自动转换为false,那么就不会被加到result中!所以我们要进行检查:
functionconcat(str1,str2,str3){ varresult=str1+str2; if(typeofstr3=="string"){ result+=str3; } returnresult; }
4.2数据类型错误
functiongetQueryString(url){ varpos=url.indexOf("?"); if(pos>-1){ returnurl.substring(pos+1); } return""; }
这个函数打算返回给定URL中的查询字符串。但如果传入非字符串类型的参数,就会导致错误。所以要加上类型判断:
functiongetQueryString(url){ if(typeofurl=="string"){ varpos=url.indexOf("?"); if(pos>-1){ returnurl.substring(pos+1); } } return""; }
还有,如果在流控制语句中使用非布尔值也会导致数据类型错误:
functionreverseSort(values){ if(values){//非数组值都会报错 values.sort(); values.reverse(); } }
另一种错误是将参数与null进行比较,这只能确保参数不是null和undefined,也不建议将参数与undefined进行比较。
还有一种错误是,只针对某一特性进行检测:
functionreverseSort(values){ if(typeofvalues.sort=="function"){ values.sort(); values.reverse(); } }
上面的函数,如果传入带有sort()方法的对象,就会通过检测,但会在调用reverse()方法时报错!所以这里最好是使用instanceof来检测:
functionreverseSort(values){ if(valuesinstanceofArray){ values.sort(); values.reverse(); } }
总的来说,基本类型的值使用typeof检测,而对象的值使用instanceof来检测。特别是面向公众的API,一定要执行类型检查,以确保函数始终能够正常执行。
4.3通信错误
JavaScript与服务器之间的任何一次通信,就有可能会产生错误。
一种通信错误是发送了不正确的URL格式数据。一般是没有使用encodeURIComponent()对数据进行编码,可以使用这里的函数来处理查询字符串:
/** *添加查询字符串 *@paramurlURL *@paramname参数名 *@paramvalue参数值 *@returns{*} */ functionaddQueryStringArg(url,name,value){ if(url.indexOf("?")==-1){ url+="?"; }else{ url+="&"; } url+=encodeURIComponent(name)+"="+encodeURIComponent(value); returnurl; }
尽量使用这个函数,就可以确保编码正确。
5区分致命错误与非致命错误
非致命错误可以根据以下列出的一个或多个条件来确定:
- 不影响用户的主要任务;
- 只影响页面的一部分;
- 可以恢复;
- 重复操作可以消除错误。
而致命错误也可以根据以下列出的一个或多个条件来确定:
- 无法继续执行;
- 影响到了用户的主要操作;
- 导致其他连带的错误。
如果发生了致命错误,要立即发送消息通知用户,告诉他们无法再继续操作咯。如果必须刷新页面才会恢复,也必须通知用户,并提供了可刷新页面的按钮。
比如,在大型网站中,可能有多个互不依赖的模块,它们是类似这样初始化的:
for(vari=0,len=mods.length;i这样做的问题是,如果有一个模块发生错误,那么就会导致后续的其它模块无法被初始化,从而导致致命的错误!我们这样改造下,让致命的错误变成非致命的错误:
for(vari=0,len=mods.length;i6把错误记录到服务器
建议集中保存错误日志,这样可以方便后续查找错误的原因。所以我们可以把JavaScript错误发送给服务器,让服务器保存起来。这样把前后端的错误集中起来管理,就可以很方便地对数据进行分析咯O(∩_∩)O~
首先先在服务器上创建一个接口,用于接收页面发送的错误数据:
/** *记录JavaScript错误 *@paramsev严重程度,比如:nonfatal *@parammsg错误消息 */ functionlogError(sev,msg){ varimg=newImage(); img.src="log.action?sev="+encodeURIComponent(sev)+"&msg="+encodeURIComponent(msg); }使用Image对象来发送请求,有这些好处:
- 所有的浏览器都支持Image对象,包含那些不支持XMLHttpRequest对象的浏览器。
- 可以避免跨域限制。比如可以使用一台日志服务器专门接收多台web服务器抛出的错误。
只要是使用了try-catch语句,就要把错误记录到日志中:
for(vari=0,len=mods.length;i第二个参数是上下文信息以及JavaScript的错误消息,带上上下文信息可以方便分析导致错误的真正原因。
更多关于JavaScript相关内容感兴趣的读者可查看本站专题:《JavaScript错误与调试技巧总结》、《JavaScript传值操作技巧总结》、《javascript编码操作技巧总结》、《JavaScript中json操作技巧总结》、《JavaScript切换特效与技巧总结》、《JavaScript查找算法技巧总结》、《JavaScript动画特效与技巧汇总》、《JavaScript数据结构与算法技巧总结》、《JavaScript遍历算法与技巧总结》及《JavaScript数学运算用法总结》
希望本文所述对大家JavaScript程序设计有所帮助。