前端的 JavaScript 开发中,发现开发者对于错误异常的处理普遍都比较简单粗暴,如果应用程序中缺少有效的错误处理和容错机制,代码的健壮性就无从谈起。本文整理出了一些常见的错误异常处理的场景,旨在为前端的 JavaScript 错误异常处理提供一些基础的指导。
先来简单介绍一下 JavaScript 中的 Error 对象,通常 Error 对象由重要的两部分组成,包含了 error.message 错误信息和 error.stack 错误追溯栈。
产生一个错误很简单,比如在 foo.js 中直接调用一个不存在的 callback 函数。
// foo.js
function foo () {
callback();
}
foo();
此时通过 Chrome 浏览器的控制台会展示如下的信息。
Uncaught ReferenceError: callback is not defined
at foo (foo.js:2)
at foo.js:5
其中 Uncaught ReferenceError: callback is not defined 就是 error.message 错误信息,而剩下的 at xxx 就是具体的错误追溯栈,在 Chrome 的控制台中,对错误的展示进行了优化。
如果我们通过 window.onerror 来捕获到该错误后将 Error 对象直接输出到页面中会展示出更原始的数据。
<!-- 展示错误的容器 -->
<textarea id="error"></textarea>
// 输出错误
window.onerror=function (msg, url, line, col, err) {
document.getElementById('error').textContent=err.message + '\n\n' + err.stack;
};
原始的错误数据中会展示出错误追溯栈中的 Source URL。
callback is not defined
ReferenceError: callback is not defined
at foo (http://example.com/js-error/foo.js:2:5)
at http://example.com/js-error/foo.js:5:1
有了错误追溯栈,就能通过发生错误的文件 Source URL 和错误在代码中的具体位置来快速定位到错误。
看起来好像很简单,但实际的开发中如何有效的捕获错误,如何有效的抛出错误都有一些需要注意的点,下面逐个的来讲解。
前端在捕获错误时都会通过绑定 window.onerror 事件来捕获全局的 JavaScript 执行错误,标准的浏览器在响应该事件时会依次提供 5 个参数。
window.onerror=function(message, source, lineno, colno, error) { ... }
使用 window.addEventListener 也能绑定 error 事件,但是该事件函数的参数是一个 ErrorEvent 对象。
绑定 window.onerror 事件时,事件处理函数的第 5 个参数在低版本浏览中或 JS 资源跨域场景下可能不是 Error 对象。
在 Chrome 浏览器中如果页面加载的 JS 资源文件中存在跨域的 script 标签,在发生错误时会提示 Script error 而缺乏错误追溯栈。
window.onerror 在响应跨域 JavaScript 错误时缺乏错误追溯栈时的 arguments 对象如下:
[
'Script error.',
'',
0,
0,
null
]
为了正常的捕获到跨域 JS 资源文件的错误,需要具备两个条件: 1. 为 JS 资源文件增加 CORS 响应头。 2. 通过 script 引用该 JS 文件时增加 crossorigin="anonymous" 的属性,如果是动态加载的 JS,可以写作 script.crossOrigin=true 。
window.onerror 能捕获一些全局的 JavaScript 错误,但还有不少场景在全局是捕获不到的。
window.onerror 能捕获全局场景下的错误,如果已知一些程序的场景中可能会出现错误,这个时候一般会使用 try/catch 来进行捕获。
但是在使用 try/catch 块时无法捕获异步错误,例如块中使用了 setTimeout 。
try {
setTimeout(function () {
callTimeout(); // callTimeout 未定义,会抛错
}, 1000);
}
catch (err) {
console.log('catch the error', err); // 不会被执行
}
try/catch 在处理 setTimeout 这类异步场景时是无效的,执行时仍会抛错,catch 中的代码不会被执行。
虽然在 try/catch 中没有捕获到,此时如果有绑定 window.onerror 则会被全局捕获。
由此可见, try/catch 应该是只能捕获 JS Event Loop 中同步的任务。
如果想正确的捕获 setTimeout 中的错误,需要将 try/catch 块写到 setTimeout 的函数中。
setTimeout(function () {
try {
callTimeout(); // callTimeout 未定义,不会抛错
}
catch (err) {
console.log('catch the error', err); // 将会被执行
}
}, 1000);
Promise 有自己的错误处理机制,通常 Promise 函数中的错误无法被全局捕获。
var promise=new Promise(executor);
promise.then(onFulfilled, onRejected);
比较容易遗漏错误处理的地方有 executor 和 onFulfilled ,在这些函数中如果发生错误都不能被全局捕获。
正确的捕获 Promise 的错误,应该使用 Promise.prototype.catch 方法,意外的错误和使用 reject 主动捕获的错误都会触发 catch 方法。
catch 方法中通常会接收到一个 Error 对象,但是当调用 reject 函数时传入的是一个非 Error 对象时,catch 方法也会接收到一个非 Error 对象,这里的 reject 和 throw 的表现是一样的,所以在使用 reject 时,最好是传入一个 Error 对象。
reject(
new Error('this is reject message')
);
值得注意的是,如果 Promise 的 executor 中存在 setTimeout 语句时, setTimeout 的报错会被全局捕获。
Async Function 和 Promise 一样,发生错误不会被全局的 window.onerror 捕获,所以在使用时如果有报错,需要手动增加 try/catch 语句。
匿名函数的使用在 JavaScript 中很常见,但是当出现匿名函数的报错时,在错误追溯栈中会以 anonymous 来标识错误,为了排查错误方便,可以将函数进行命名,或者使用函数的 displayName 属性。
函数如果有 displayName 属性,在错误栈中会展示该属性值,如果用于命名重要的业务逻辑属性,将有效帮助排查错误。
上面说了很多错误捕获的注意点,如果要主动的抛错,都会使用 throw 来抛错,常见的几种抛错方法如下:
throw new Error('Problem description.') // 方法 1
throw Error('Problem description.') // 方法 2
throw 'Problem description.' // 方法 3
throw null // 方法 4
其中方法 1 和方法 2 的效果一样,浏览器都能正确的展示错误追溯栈。方法 3 和方法 4 不推荐,虽然能抛错,但是在抛错的时候不能展示错误追溯栈。
try/catch 和 throw ,一个用来捕获错误,一个用来抛出错误,如果两个结合起来用通常等于脱了裤子放屁多此一举,唯一有点用的是可以对错误信息进行再加工。
可以在 Chrome 控制台中模拟出一个结合使用的实际场景。
try {
foo();
}
catch (err) {
err.message='Catch the error: ' + err.message;
throw Error(err);
}
由于在 catch 块中又抛出了错误,所以该错误没有被捕获到,但此时错误信息经过了二次封装。
Uncaught Error: ReferenceError: Catch the error: foo is not defined
通过对错误信息的二次封装,可以增加一些有利于快速定位错误的额外信息。
原作者:雨夜带刀's Blog
当javascript代码中出现错误的时候,js引擎就会根据js的调用栈逐级寻找对应的catch,如果没有找到相应的catch handler或catch handler本身又有error或者又抛出新的error,最后就会把这个error的处理交给浏览器,并显示在错误控制台中)显示错误信息给访问者。
是js提供的异常处理机制,用法如下:
try {
// 这段代码从上往下运行,其中任何一个语句抛出异常该代码块就结束运行}
catch (e) {
// 如果try代码块中抛出了异常,catch代码块中的代码就会被执行。
// e是一个局部变量,用来指向Error对象或者其他抛出的对象
}
finally {
//无论try中代码是否有异常抛出(甚至是try代码块中有return语句),
//finally代码块中始终会被执行
}
顾名思义,典型的语法错误。
function foo{
}
if{}
Js代码是从上往下依次执行,但是js引擎先解析代码是否语法出错,如果语法都解析不通过,那么显而易见:一行代码也不会执行,从而会在控制台中输出语法报错:
syntaxError错误
变量未定义(也叫未声明)指的是,当程序中使用了一个未定义的变量则会报错。
如下代码:
var foo=1
var bar=foo + n1
显而易见,n1变量是未定义的就直接使用,从而会在控制台中输出如下错误:
XXX is not defined 变量未定义
TypeError错误指的是数据类型未正确使用。
例如一:
var foo=function(){
console.log('foo')
}
foo='hello world'
foo()
在某些逻辑下,foo本身存储的是函数,但是误把foo赋值了一个字符串或其它不是函数的数据类型,但是foo当作函数来调用了,则会报TypeError错误在控制台中输出:
TypeError,xxx is not a function
例如二:
未正确获取元素,导致得到一个null而不是DOM节点对象后,绑定事件而引发的TypeError错误。
<script>
var oBtn=document.getElementById('btn')
//因为代码从上往下解析的原因,还未解析到button标签,返回为null。
//null是空对象,不能绑定任何属性,onclick虽然是事件,
//但也是对象中属性的一部分,所以报TypeError错误。
oBtn.onclick=function(){
console.log('bar')
}
</script>
<button id="btn">foo</button>
cannot set property 'onclick' of null
正确错误是把选择元素的js代码放置html标签之后,也就是紧邻 </body>标签,或放在windo.onload事件中。
<script>
window.onload=function(){
var oBtn=document.getElementById('btn')
//因为代码从上往下解析的原因,还未解析到button标签,返回为null。
//null是空对象,不能绑定任何属性,onclick虽然是事件,
//但也是对象中属性的一部分,所以报TypeError错误。
oBtn.onclick=function(){
console.log('bar')
}
}
</script>
<button id="btn">foo</button>
首先,我们需要了解JSON是什么 ?
JSON(JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式。它基于 ECMAScript (欧洲计算机协会制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。
而它的定义规则和js中字面量声明对象很像,所以很多初学者以为json就是js对象,其实这是错误的。
key : value
名称/值包括字段名称(在双引号中),后面写一个冒号,然后是值:
"name" : "foo"
前方高能~~~
谈到这里,json数据从哪来呢?
在请求Ajax的时候,会从后台服务器中拿到json数据,往往会把json解析成js对象。则前端工程师会用到JSON.parse方法。有时候前端也会定义JSON数据,如果语法不正确当转成js对象时,则会报错。如下代码:
//var foo='{ name:"bar" }'//name未带双引号
var foo='{ "name":bar }'//bar未带双引号
var f=JSON.parse( foo )
token n in JSON at position
正确的JSON转换js对象的方式如下:
var foo='{ "name":"bar","age":20 }'//20无需带,则理解为数值类型
var f=JSON.parse( foo )
console.log( f ) //{name: "bar", age: 20} ,此时可以正确的把json转换成js对象,
//通过 点 语法,也就是f.name和f.age访问到具体的数据
以上是JavaScript中常见的错误,后期遇到会不断更新,感谢小伙伴们的踊跃投稿和留言。
内容是《Web前端开发之Javascript视频》的课件,请配合大师哥《Javascript》视频课程学习。
错误处理对于web应用开发至关重要,任何javascript错误都有可能会导致网页无法使用,因此作为开发人员,必须要及时处理有可能出现的错误;
从IE4.0之后,几乎所有的浏览器都包含了一些基本的错误处理功能,但并没有统一,后来,由ECMAscript添加了异常处理机制,也就是try…catch…finally结构以及throw操作;
错误处理的重要性: 好的错误处理技术可以让脚本的开发、调试和部署更加流畅,能对代码进行更好的控制;另外,JS缺乏标准的开发环境;
错误类型:
语法错误(syntax error):也称为解析错误,发生在传统编程语言的编译解释时;发生语法错误时,就会发生阻塞,也就是不能继续执行代码,但只是同一个线程中的代码会受影响,其他线程中的代码不受影响;
// Uncaught SyntaxError: Invalid or unexpected token
// 未捕获的语法错误:无效或意外的标记
document.write("zeronetwork;
运行时错误(Runtime error):也称为exception异常, 其发生在编译期/解释期后,此时,问题并不出现在代码的语法上,而是在尝试完成一个非法的操作;
<input type="button" value="单击" onclick="handleClick()" />
<script>
// Uncaught ReferenceError: openMy is not defined
// 未捕获的引用错误:未定义openMy
function handleClick(){
openMy();
}
</script>
错误报告:
因为每个浏览器都有自己的内置Javascript解释程序,所以每种浏览器报告错误的方式都不同;有些是弹出错误信息,有些是把信息打印在控制台中;
IE(windows): 默认情况下,会弹出包含错误细节的对话框,并询问是否继续执行页面上的脚本;如果浏览器有调试器(如:Microsoft Script Debugger) ,此对话框会提供一个是调试还是忽略的选项;如果在IE设置中取消了”显示错误”,那么会在页面左下角显示一个黄色的图标;
注:如果JS代码就在HTML里,显示错误行是正确的,如果是外部的JS文件,则行号往往差一行,如第5行则为第4行;
Mozilla(所有平台): 在控制台中打印错误信息,并发出警告;其会报告三种类型的消息:错误、严格警告和消息等的;
Safari (MacOS):是对JavaScript错误和调试的支持最差,默认情况下,它对终端用户不提供任何javascript错误报告;
错误处理:
Javascript提供了两种处理错误的方式:
onerror事件处理函数:
window对象的onerror属性是一个事件处理程序,页面上出现异常时,error事件便在window对象上触发,并把错误消息输出到Javascript控制台上,这种方式也称为全局错误捕获;如:
window.onload=function(){
show(); // 在onload事件中调用了一个不存在的函数
}
window.onerror=function(){
alert("出现错误");
return true;
}
获取错误信息:
window.onerror事件处理程序在调用时可以传5个参数,由这5个参数可以获取详细的错误信息;
window.onerror=function(sMessage, sUrl, sLine, sColumn, error){
console.log("Error:" + sMessage + " URL:" + sUrl + " Line:" + sLine + " Column:" + sColumn);
console.log(error);
return true;
}
onerror处理程序的返回值:
如果返回true,则阻止执行默认的事件处理程序,也就是将通知浏览器,事件处理程序已经处理了错误,不需要其他操作,反之会显示错误消息;
某些元素也支持onerror; 但其处理函数没有任何关于error信息的参数,如:
document.images[0].onerror=function(event){
console.log(event); // Event
console.log(event.type); // error
}
这里的event参数是一个类型为Event事件对象,其存储的信息除了type返回了error,并没有其他和错误相关的信息;
全局错误处理window.onerror通常不能恢复脚本继续执行,但会给开发者发送错误信息;
window.onerror=function(error){
console.log(error);
}
show();
console.log("中止,不会被执行");
window.onload=function(){
console.log("也不会被执行");
}
可以是简单的打印,也可以把错误保存到日志记录里;
window.onerror就是绑定在window对象的error事件,也可以使用标准的添加事件侦听的方式window.addEventListener(eventtype, handler),其需要两个参数,eventtype为事件类型,在此为error,handler是事件处理函数,其需要一个参数event,一个ErrorEvent类型的对象,其保存着有关事件和错误的所有信息,如:
window.addEventListener("error", function(event){
console.log(event); // ErrorEvent
console.log(event.error); // Error对象
console.log(event.error.name);
console.log(event.error.message);
console.log(event.error.stack);
console.log(event.lineno); // 行
console.log(event.colno); // 列
console.log(event.filename);
});
在实际的开发中,这两种方式都会被使用,只不过addEventListener有定的兼容必问题,所以要兼顾所有的浏览器且不太关注事件对象本身的话,就使用window.onerror;
当加载自不同域的脚本中发生语法错误时,为避免信息泄露,语法错误的细节将不会报告,只会返回简单的"Script error.";
<script>
window.onerror=function(msg, url, lineNo, columnNo, error){
console.log(msg); // Script error
console.log(url); // ""
console.log(lineNo); // 0
console.log(columnNo); // 0
console.log(error); // null
}
</script>
<script src="https://www.zeronetwork.cn/demo/demo.js"></script>
可以针对同域和不同域的错误分开处理,如:
<script>
window.onerror=function(msg, url, lineNo, columnNo, error){
var str_error=msg.toLowerCase();
var sub_string="script error";
if(str_error.indexOf(sub_string) > -1)
alert("脚本发生错误,详情请在控制台查看");
else{
var message=[
'消息:' + msg,
'URL:' + url,
'行:' + lineNo,
'列:' + columnNo,
'错误对象:' + error
].join(" - ");
alert(message);
}
}
show();
</script>
<script src="https://www.zeronetwork.cn/demo/demo.js"></script>
从上在的执行结果来看,error事件执行了两次,原因是使用了两个script,也就是当一个script有错误发生时,它只会阻止当前的script块,而不会阻止其他的script块;如:
<script>
show(); // 会捕获
console.log("不会被执行");
myshow(); // 不会捕获
</script>
<script src="https://www.zeronetwork.cn/demo/demo.js"></script>
<script>
console.log("执行了");
demo(); // 会捕获
console.log("不会被执行");
</script>
body元素的onerror特性,也可以充当事件处理函数,如:
<body onerror="alert('出现了错误');return true;">
注意,先注释掉window.onerror等代码;
此时,可以直接使用event、source、lineno、colno、error等属性;
<body onerror="alert(event + '\n' + source + '\n' + lineno + '\n' + colno + '\n' + error);return true;">
当然了,也可以为body绑定error事件,此时各属性,必须明确指定,如:
document.body.onerror=function(msg, url,lineno,colno,error){
alert(msg + '\n' + url + '\n' + lineno + '\n' + colno + '\n' + error);
return true;
}
try-catch语句:
try语句中为期待正常执行的代码块,当在try语句中发生错误,其余代码会中止执行,catch语句就处理该错误,如果没有错误,就跳过catch语句;try和catch必须成对出现;
try{
//code
[break]
}catch([exception]){
//code
}[finally]{
//code
}
// 如
try {
show();
alert("不能执行");
} catch (error) {
alert("出现一个错误:" + error);
} finally{
alert("管你呢");
}
try语句块内的错误只会中止try语句块中发生错误之后的逻辑代码,并不会中止整个脚本的运行;执行try-catch语句,必须是运行时,运行时错误,也被称为异常;try-catch语句中指定只能有一个catch子句;try-catch语句适合处理无法预知、无法控制的错误;finally常被用于无论结果是否有异常,都要执行的代码,如:
try{
alert("try");
show();
alert("no exec");
}catch(error){
alert("catch");
}finally{
alert("finally");
}
alert("continute");
代码执行的两条路径:如果没有异常,执行路径为:try->finally,反之为:try的部分->catch->finally;
一般用于关闭打开的链接和释放资源;
var connection={open: function(){},close: function(){},send: function(data){}}
// var data="大师哥王唯"; // 注释这一行,让它产生异常
connection.open();
try{
connection.send(data);
}catch(exception){
console.log("出现一个错误");
}finally{
connection.close();
console.log("关闭了");
}
还有一个典型的应用,读写文件,如:
function openFile(){};
function writeFile(data){};
function closeFile(){};
openFile();
try{
writeFile();
}catch(error){
console.log(error);
}finally{
closeFile();
}
在try-catch-finally语句块中的变量是全局变量:
try{
var name="王唯";
show();
var city="蚌埠";
}catch(error){
var age=18;
console.log(name);
}finally{
var sex="男";
console.log(name);
console.log(age);
}
console.log(name);
console.log(city);
console.log(age);
console.log(sex);
try-catch-finally与return:
如果直接在try-catch-finally语句块中执行return,会抛出异常,如:
try {
console.log("try");
// return; // Illegal return statement 非法返回语句
console.log("try agin");
} catch (error) {
console.log(error);
// return; // Illegal return statement
}finally{
console.log("finally");
// return; // Illegal return statement
}
如:
function foo(){
try {
console.log("try");
return 1;
show();
console.log("try agin");
} catch (error) {
console.log(error);
return 2;
}finally{
console.log("finally");
return 3;
}
}
console.log(foo()); // 3
try-finally:
没有catch从句,只有try-finally也可以,目的是,只确保执行开始和最终的过程,而不处理错误,如:
try{
console.log("try");
show();
}finally{
console.log("finally"); // 会执行
}
console.log("over"); // 不会执行,已中止
但此时,还是会抛出异常的,但此时,会在执行完finally后中止执行,并会查找外部的catch语句;
嵌套try-catch语句:
在catch子句中,也有可能会发生错误,所以就可以使用嵌套的try-catch语句,如:
try {
show();
console.log("不能执行");
} catch (error) {
console.log("出现一个错误:" + error);
try {
var arr=new Array(10000000000000000);
arr.push(error);
} catch (error) {
console.log("又出现了一个错误:" + error);
}
} finally{
console.log("管你呢");
}
也可以在try中嵌套try-catch-finally语句,如:
try{
try{
console.log("try");
show();
}catch(error){
console.log("error");
}finally{
console.log("finally");
}
}catch(error){
console.log(error);
}
一个比较典型的应用,就是处理json数据,如:
// var json='{"name":"wangwei", "age": 18, "sex": "男"}';
var json='{bad json}'; // Uncaught SyntaxError
var data=JSON.parse(json);
console.log(data.name);
console.log(data.age);
console.log(data.sex);
一量json数据发生错误,整个应用都会崩溃,所以应该使用try-catch,如:
<div id="msg">您的信息:</div>
<script>
window.onload=function(){
var msg=document.getElementById("msg");
try{
// var json='{"name":"王唯", "age": 18, "sex": "男"}';
var json='{bad json}'; // Uncaught SyntaxError
var data=JSON.parse(json);
msg.innerHTML +="姓名:" + data.name + ",年龄:" + data.age + ",性别:" + data.sex;
}catch(error){
msg.innerHTML="开小差了,找不到你的信息";
}
}
</script>
当使用了try-catch语句,就不会将错误提交给浏览器,也就不会触发error事件,如:
window.onerror=function(error){
console.log(error); // 不会触发
}
try{
show();
}catch(error){
console.log(error);
}
Error错误对象:
在catch中会捕获一个Error错误对象;该对象在Javascript解析或运行时,一旦发生错误,引擎就会抛出这个对象;如果没有相关联的try-catch捕获该对象,就由浏览器输出这个对象;
// ...
console.log("错误:" + error + " name:" + error.name + " message:" + error.message);
也可以通过Error的构造器创建一个错误对象,这个Error对象也可用于用户自定义的异常;语法:new Error([message[, filename[, lineNumber]]]);
实例化Error对象,也可以不使用new关键字,如:
var error=new Error("自定义错误对象");
var error=Error("不使用new");
console.log(error);
console.log(error.name); // Error
console.log(error.message); // 自定义错误对象
Error错误对象属性:
Error类还有6个子类,其可以通过错误对象的name属性返回具体异常类的名称:
// EvalError
try{
throw new EvalError("Eval异常");
}catch(error){
console.log(error);
console.log(error instanceof EvalError); // true
console.log(error.name); // EvalError
console.log(error.message); // Eval异常
}
// RangeError
var num=1;
try{
num.toPrecision(500); // [pr??s??n] 精度
}catch(error){
console.log(error);
}
// ReferenceError
var x;
try {
x=y + 1;
} catch (error) {
console.log(error);
}
// SyntaxError
try{
eval("alert('wangwei)");
}catch(error){
console.log(error);
}
// TypeError
var num=1;
try{
num.toUpperCase(); // 无法将数字转为大写
}catch(error){
console.log(error);
}
// URIError (malformed [?m?l?f??md]格式不正确,畸形的)
try{
decodeURI("%%%"); // 使用了非法字符
}catch(error){
console.log(error);
}
Error对象的message属性是浏览器生成的用于表示错误描述的信息,因为这个属性是特定于浏览器的,所以不同的浏览器上可能产生不同的错误信息,如:
try {
eval("a ++ b");
show();
console.log("执行了吗?");
} catch (error) {
// SyntaxError:Unexpected identifier或
// SyntaxError:unexpected token: identifier
console.log(error.name + ":" + error.message);
}
使用name属性判断错误类型:
try {
eval("a++b");
} catch (error) {
console.log(error instanceof SyntaxError); // true
if(error.name=="SyntaxError")
console.log(error.name + ":" + error.message);
else
console.log("未知错误:" + error.message);
}
抛出异常:
throw语句的作用是手动中断程序执行,抛出一个错误,一般用于有目的的抛出异常,也就是允许开发者可以创建自定义错误;
throw可以抛出任何类型的值,也就是说,它的参数可以是任何值;语法:throw error_object;error_object可以是字符串、数字、布尔或对象;
throw "出现一个错误";
throw 50666;
throw true;
throw new Object();
throw {
toString:function(){
return 'Error!';
}
}
function getRectArea(width, height){
if(isNaN(width) || isNaN(height))
throw '参数应该是number类型';
return width * height;
}
getRectArea("wangwei",10);
对于Javascript引擎来说,只要遇到throw语句,程序就会终止;
也可以抛出一个Error错误对象;Error对象的构造函数只有一个参数,
throw new Error("请再次尝试");
其他Error子类对象也可以抛出:
throw new SyntaxError("...");
throw new TypeError("...");
throw new RangeError("...");
throw new EvalError("...");
throw new URIError("...");
throw new ReferenceError("...");
对于Error类和其子类来说,错误对象的name就是其构造函数的名称,message是其构造函数的参数;
当抛出异常后,throw之后的语句将不会执行,并跳到相关联的catch语句中进行处理,如:
<h1>请输入18-99之间的数字</h1>
<input id="txtInput" type="text" />
<button id="btn">确定</button>
<p id="msg"></p>
<script>
function myFun(){
var msg,x;
msg=document.getElementById("msg");
msg.innerHTML="";
x=document.getElementById("txtInput").value;
try{
if(x=="") throw "空的";
if(isNaN(x)) throw "不是数字";
x=Number(x);
if(x < 18) throw "太小";
if(x > 99) throw "太大";
msg.innerHTML="输入的值正确:" + String(x);
}
catch(error){
msg.innerHTML="输入的值不正确:" + error;
}
}
var btn=document.getElementById("btn");
btn.onclick=myFun;
</script>
也可以在某个语句块的外部捕获throw异常,如:
function sum(a,b){
if(arguments.length < 2)
throw new Error("需要两个参数");
else
return a + b;
}
try{
console.log(sum(18));
}catch(error){
// Error:需要两个参数
console.log(error.name + ":" + error.message);
}
可以通过instanceof判断异常的类型来特定处理某一类的异常,例如可以区分浏览器抛出的异常和开发人员抛出的异常,如:
function sum(a,b){
if(arguments.length < 2)
throw new Error("需要两个参数");
if(isNaN(a) || isNaN(b))
throw "参数是不是Number类型";
return a + b;
}
try{
console.log(sum(18,12));
}catch(error){
if(error instanceof SyntaxError)
console.log("语法错误:" + error.name + ":" + error.message);
else if(error instanceof Error)
console.log(error.name + ":" + error.message);
else
console.log(error);
}
注:判断Error类型要放到if的最后一个条件;
即使在catch语句中,还可以根据实际情况,再次抛出异常,此时,其可以被外部的try-catch语句块捕获(如果存在的话);
当发生异常时,代码会立即停止,仅当有try-catch语句捕获到异常时,代码才会继续执行;其背后运行的原理是,当发生异常,JavaScript解释器会立即停止执行的逻辑,并跳转到就近的try-catch异常处理程序,如果发生异常的代码块中没有相关联的catch从句,解释器会检查更高层的闭合代码块,看它是否有相关联的异常处理程序,以此类推,直到找到一个异常处理程序为止;如果发生异常的函数中没有处理它的try-catch语句,异常将向上传播到调用该函数的代码,如此,异常就会沿着Javascript的语法结构或调用栈向上传播;如果没有找到任何异常处理程序,JavaScript将把异常当成程序错误来处理,并通过浏览器报告给用户;
自定义错误类型:
可以基于Error类来创建自定义的错误类型,此时可以使用throw抛出自定义的异常类,或通过instanceof来检查这个异常类的类型,新类型需要实现name和message属性;
function CustomError(message){
this.name="CustomError";
this.message=message || 'Default Message';
this.stack=(new Error()).stack;
}
// CustomError.prototype=new Error();
// 或者
CustomError.prototype=Object.create(Error.prototype);
CustomError.prototype.constructor=CustomError;
try{
var name="jingjing";
if(name !=="wangwei")
throw new CustomError("自定义的错误类型");
}catch(error){
console.log(error.message);
}
小示例:
function UserException(message){
this.name="UserException";
this.message=message;
}
function getMothName(m){
m=m - 1; // 调整月份数字到数组索引(1=Jan,12=Dec)
var months=['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
if(months[m] !=undefined)
return months[m];
else
throw new UserException("Invalid Month No");
}
try{
var myMonth=15;
var monthName=getMothName(myMonth);
}catch(error){
var monthName="未知";
console.log(error.name + ":" + error.message);
}
小示例:验证电话号码,如:
function Telephone(num){
num=String(num);
var pattern=/^((0\d{2,3}-\d{7,8})|(1[3584]\d{9}))$/;
if(pattern.test(num)){
this.value=num.match(pattern)[0];
this.valueOf=function(){
return this.value;
};
this.toString=function(){
return String(this.value);
}
}else{
throw new TelephoneFormatException(num);
}
}
function TelephoneFormatException(value){
this.name="TelephoneFormatException";
this.message="电话号码格式不正确";
this.value=value;
this.toString=function(){
return this.value + ":" + this.message;
}
}
// 应用
var TELEPHONE_INVALID=-1;
var TELEPHONE_UNKNOWN_ERROR=-2;
function verifyTelephone(num){
try{
num=new Telephone(num);
}catch(error){
if(error instanceof TelephoneFormatException)
return TELEPHONE_INVALID;
else
return TELEPHONE_UNKNOWN_ERROR;
}
return num.toString();
}
console.log(verifyTelephone("010-66668888"));
console.log(verifyTelephone("13812345678"));
console.log(verifyTelephone("138123456")); // -1
console.log(verifyTelephone("wangwei")); // -1
常见错误:
由于javaScript是松散类型的,也不会验证函数的参数,因此错误只会在运行时出现;一般来说,需要关注三种错误:类型转换错误、数据类型错误、通信错误;
类型转换错误:
一般发生在使用某个操作符,或者使用其他可能自动转换值的数据类型的语言结构时;
function output(str1,str2,str3){
var result=str1 + str2;
if(str3)
result +=str3;
return result;
}
console.log(output(1,2,3));
console.log(output(1,2));
console.log(output(1,2,0));
console.log(output(1,2,"wangwei"));
这就是一个非常典型的与期望不一致的方式;
数据类型错误:
在流控制语句中使用非布尔值,是极为常见的一个错误来源,为避免此类错误,就要做到在条件比较时确定传入的是布尔值,例如,把if语句改成:if(typeof str3=='number');
所以在使用某个变量或对象时,一定要适当地检查它的数据类型,如:
function reverseSort(values){
// if(values){
// values.sort();
// values.reverse();
// }
if(arguments.length > 0){
if(!Array.isArray(values))
return [];
else{
values.sort();
values.reverse();
return values;
}
}
return [];
}
var arr=[3,2,6,9,4];
// var arr=100; // Uncaught TypeError: values.sort is not a function
console.log(reverseSort(arr));
另一个常见的错误就是将参数与null值进行比较。与null进行比较只能确保相应的值不是null和undefined。要确保传入的值有效,仅检测null值是不够的;
function reverseSort(values){
// if(values !=null){ // 任何非数组值都会导致错误
if(values instanceof Array) // 非数组值被忽略
values.sort();
values.reverse();
}
return values;
}
var arr=[3,2,6,9,4];
// var arr=100; // Uncaught TypeError: values.sort is not a function
console.log(reverseSort(arr));
// 或
function reverseSort(values, fun){
if(values instanceof Array){
if(fun !=null && typeof fun==="function")
values.sort(fun);
else
values.sort();
}
return values;
}
var arr=[3,2,6,9,4];
console.log(reverseSort(arr, function(a,b){
return a > b ? -1 : 1;
}));
通信错误:最典型的就是Ajax应用,用其可以动态加载信息,但是,javascript与服务器之间的任何一次通信,都有可能会产生错误;
调试技巧:
使用警告框: 这是最简单、流行的方式,如:
function test(){
alert("函数内");
var iNum1=5, iNum2=10;
alert(iNum1);
var iResult=iNum1 + iNum2;
alert(iResult);
}
test();
抛出自定义错误:
function assert(bCondition, sErrorMessage){
if(!bCondition)
throw new Error(sErrorMessage);
}
function divide(iNum1, iNum2){
assert(arguments.length==2, "divide需要两个参数");
assert((!isNaN(iNum1) && !isNaN(iNum2)), "需要Number类型");
return iNum1 / iNum2;
}
console.log(divide(10,2));
console.log(divide(10,"c")); // 异常
// 或
try{
console.log(divide(10,"c"));
}catch(error){
console.log(error.name + ":" + error.message);
}
Javascript校验器:
jslint的主要目的是指出不合规范的js语法和可能的语法错误,包括一些不良代码;官网:http://www.jslint.com/
如以下,会给出警告:
调试器:
Javascript自身不具备调试器,但目前所有的浏览器可以使用自身的调试器;
IE调试:
启用IE的调试功能:
菜单“工具”|“Internet选项”命令,打开“Internet选项”对话框,在“高级”选项卡中,找到两个“禁用脚本调试”复选框并取消;开始调试,调试的主要工作是反复地跟踪代码,找出错误并修正;
设置断点:
在调试程序窗口中,将光标移动到需要添加断点的行上,按一次F9键或单击,当前行的背景色变为红色,并且在窗口左边界上标上红色的圆点,当程序运行到断点时就会暂停;
运行调试:
单击继续或按F5进行逐步运行调试;F10步进、F11步入,都可以继续向下执行;将鼠标移动到变量名上时,会显示变量当前时刻的值;或者在右侧的“监视”窗格中可以观察该变量的值;点击变量信息框中的变量值或右侧“监视”空格中的变量值可以修改变量的当前值;更多的调试操作:查看调用关系、监视特定变量的值等;
// 示例
var balance=200.0; //
var willPay=20.0;
function pay(_balance, _pay){
return _balance - _pay;
}
function showBalance(){
debugger;
var blnc=pay(balance,willPay);
alert("当前余额:" + blnc);
}
showBalance();
日志输出:
程序运行时,有些中间数据需要记录,以便检查程序运行的状态;对于JavaScript记录中间数据通常是以日志的形式记录需要记录的数据,再发送到服务器上保存起来;日志记录的内容可以是任意的信息,根据开发者的需要而定;
*请认真填写需求信息,我们会在24小时内与您取得联系。