JavaScript错误处理操作实例详解


Posted in Javascript onJanuary 04, 2019

本文实例讲述了JavaScript错误处理操作。分享给大家供大家参考,具体如下:

良好的错误处理机制可以让用户得到及时的提醒,所以让我们来看看 JavaScript 提供了哪些针对错误处理的工具和方法吧O(∩_∩)O~

1 try-catch 语句

ECMA-262 第 3 版引入了 try-catch 语句,这时 JavaScript 处理异常的标准方式:

try{
  //可能会导致错误的代码
} catch (error){
  //错误处理
}

如果 try 块中的代码发生了错误,会立即执行 catch 块的代码。 catch 块有一个包含错误信息的对象,它有一个 message 属性,表示的是浏览器给出的错误消息:

<script type="text/javascript">
  try {
    window.someNonexistenceFunction();
  } catch (error) {
    console.log(error.message);
  }
</script>

message 属性是所有的浏览器都支持的属性,所以在跨浏览器编程中,最好只使用这个属性。

1.1 finally 子句

finally 子句是可选的,如果使用了 finally 子句,里面包含的代码是绝对会被执行的!甚至连 return 语句都无法阻止它被执行:

<script type="text/javascript">
  function testFinally() {
    try {
      return 2;
    } catch (error) {
      return 1;
    } finally {
      return 0;
    }
  }
  console.log(testFinally());//0
</script>

IE7 及更早的版本有一个 bug:除非有 catch 子句,否则 finally 中的代码永远不会被执行!IE8 修复了这个 bug。

注意:只要在代码中使用了 finally 子句,那么不管 return 放在 try 还是 catch 语句中,都会被忽略!

1.2 错误类型

当错误发生时,会抛出相应类型的错误对象。ECMA-262 定义了 7 种错误类型:

  • Error
  • EvalError
  • RangeError
  • ReferenceError
  • SyntaxError
  • TypeError
  • URIError

1.2.1 Error

Error 是基类型,即其他的错误类型都是从 Error 继承来的。可以利用 Error 来自定义错误类型。

1.2.2 EvalError

EvalError 是在使用 eval() 函数发生异常时被抛出。怎么才算是异常呢?如果没有把 eval() 当作函数来使用,就会抛出 EvalError:

new eval();//抛出 EvalError
eval = foo;//抛出 EvalError

实际开发中很少这样使用 eval() 函数的(除非脑袋秀逗了O(∩_∩)O~),所以很少会遇到 EvalError。

1.2.3 RangeError

RangeError 会在数值超出规定范围时被抛出。比如在定义数组时,指定了数组不支持的数组项数,就会抛出这个错误:

var item1 = new Array(-20);//抛出 RangeError
var item1 = new Array(Number.MAX_VALUE);//抛出 RangeError

1.2.4 ReferenceError

找不到对象时,会抛出 ReferenceError(这就是浏览器的知名的 “object expected” 错误)。一般在访问不存在的变量时,会抛出这个错误:

var obj = x;//x 还未被声明,所以抛出 ReferenceError

1.2.5 SyntaxError

如果把带着错误语法的字符串传入 eval() 函数时,就会抛出 SyntaxError:

eval("a ++ b");//抛出 SyntaxError

eval() 函数之外的语法错误,会导致 JavaScript 立即停止执行,所以不会抛出这个错误!

1.2.6 TypeError

如果变量中保存着意外的类型,或者访问不存在的方式时,就会抛出 TypeError。这个错误比较常见:

var o = new 10;//抛出 TypeError
alert("name" in true);//抛出 TypeError
Function.prototype.toString.call("name");//抛出 TypeError

如果传递给函数的参数类型与预期类型不符,也会抛出这个错误。

1.2.7 URIError

使用 encodeURI() 或者 decodeURI() 时,URI 的格式不正确,就会抛出 URIError 错误。一般很少发生,因为这两个函数有着非常高的容错性O(∩_∩)O~

可以针对这些错误类型进行恰当的处理:

try{
  someFunction();
} catch (error){
  if (error instanceof TypeError){
    //处理类型错误
  }
  ...
}

因为包含在 message 中的消息会因浏览器而异,所以在跨浏览器编程中,最好直接检查这些错误类型。

1.3 合理使用 try-catch

try-catch 最适合用于那些我们无法控制的错误,比如使用了一个开源库中的函数,而我们无法修改源代码的情况。

而对于自定义的函数,验证函数参数类型是否合法的情况,就不应该使用 try-catch,因为这个函数是我们可以修改的,在使用参数前先进行验证才是正途。

2 抛出错误

throw 操作符可以抛出自定义的错误,必须要指定一个值,这个值可以是任意类型。代码在遇到 throw 操作符时,会立即停止执行。只有在 try-catch 语句捕获到被抛出的值时,才会继续执行。

通过使用之前说过的内置错误类型,可以模拟浏览器错误。这些错误类型的构造函数都接收一个参数,即实际的错误消息:

throw new Error("Something bad happened.");

最常使用这些错误类型来创建自定义的错误消息:ErrorRangeErrorReferenceError TypeError

也可以利用原型链,通过继承 Error 来创建自定义消息。比如,这里为新创建的错误类型指定了 name 和 message 属性:

<script type="text/javascript">
  function CustomError(message) {
    this.name = "CustomError";
    this.message = message;
  }
  CustomError.prototype = new Error();
  throw new CustomError("My message");
</script>

这样创建的自定义错误与浏览器错误并不同,所以可是很有用的哦O(∩_∩)O~

注意: IE 只有在抛出 Error 对象时才会显示自定义的错误消息!其他错误对象,它只会显示 “exception thrown and not caught”。

2.1 抛出错误的时机

应该在已知的、某种特定错误条件下(会导致函数无法正常执行),抛出自定义的错误:

<script type="text/javascript">
  function process(values) {
    if (!(values instanceof Array)) {
      throw new Error("process():Argument must be an array.");
    }
    values.sort();
    for (var i = 0, len = values.length; i < len; i++) {
      if (values[i] > 100) {
        return values[i];
      }
    }
    return -1;
  }
  process("hi");
</script>

如果给上面的函数传入一个字符串参数,那么 sort() 就会抛错,所以我们在这里加入了带有适当消息的自定义错误。这对于一个有着几千行脚本代码的项目来说,可以显著地提升代码的可维护性O(∩_∩)O~

错误消息时包含了函数名称以及为什么会发生错误的明确描述,是不是很清晰呀O(∩_∩)O~

在开发时,要注意函数可能失败的因素,把这些因素都加上自定义错误吧!良好的错误处理机制是确保代码只发生我们定义的错误。

2.2 抛出错误与使用 try-catch

建议在抛出错误时尽量提供详尽的信息,因为我们抛出错误不就是为了在维护时,能够知道错误发生的具体位置和具体原因的吗?

3 错误事件

任何没有通过 try-catch 处理的错误都会触发 window 对象的 error 事件(IE、Firfox 和 chrome 支持)。它接收 3 个参数:错误消息、错误所在的 URL 和行号。一般只有错误消息有用。只能通过 DOM0 级技术来指定 onerror 事件处理程序,如果返回 false,就可以阻止浏览器报告错误的默认行为:

<script type="text/javascript">
  window.onerror= function (message,url,line) {
    console.log(message);
    return false;
  }
</script>

这个函数其实就是整个文档的 try-catch 语句,它可以捕获所有没有被处理的运行时错误。理想情况下,错误都被包含在 try-catch 语句中,所以不会使用到它。

注意: 不同的浏览器下,这个函数的处理方式不同。在 IE 中,发生了 onerror 事件,代码仍会执行,所有的变量和数据都会保留。但在 Firefox 中,代码会停止执行,而且之前所有的变量和数据都会被销毁!

图像也支持 error 事件。如果图像的 src 属性中的 URL 无法返回被识别的图像格式时,就会触发持 error 事件。这时的 error 事件会返回一个以图像为目标的 event 对象:

<script type="text/javascript">
  var image = new Image();
  EventUtil.addHandler(image, "load", function (event) {
    console.log("Image loaded!");
  });
  EventUtil.addHandler(image, "error", function (event) {
    console.log("Image not loaded!");
  });
  image.src = "smilex.gif";//实际并不存在
</script>

注意,如果发生了 error 事件,那么这个图像的下载过程就已经结束咯。

4 常见的错误类型

因为 JavaScript 是松散类型的语言,所以错误只会在代码运行期间发生。一般情况下,我们需要重点关注以下三种错误:

① 类型转换错误
② 数据类型错误
③ 通信错误

4.1 类型转换错误

在使用相等(==)或不相等(!==),或者在 if、for 或 while 中使用了非布尔值时,最常发生类型转换错误。

所以建议使用全等(===)和不全等(!==)来避免类型转换操作。

if 等语句会自动把任何值都转换为布尔值:

function concat(str1, str2, str3){
  var result = str1 + str2;
  if (str3){//不要这样!!!
    result +=str3;
  }
  return result;
}

这个函数是想拼接两个或三个字符串,然后返回结果。所以这里的第三个参数是可选的,,所以必须要检查。如果我们在调用这个函数时,只用到前两个参数,这意味着第三个参数因为是未使用过的命名变量,所以被自动赋值给 undefine,而 undefine 在 if 中会被自动转换为 false,这样可以。但是,如果第三个参数传入数值 0,在 if 中会被自动转换为 false,那么就不会被加到 result 中!所以我们要进行检查:

function concat(str1, str2, str3){
  var result = str1 + str2;
  if (typeof str3 == "string"){
    result +=str3;
  }
  return result;
}

4.2 数据类型错误

function getQueryString(url){
  var pos = url.indexOf("?");
  if (pos > -1){
    return url.substring(pos + 1);
  }
  return "";
}

这个函数打算返回给定 URL 中的查询字符串。但如果传入非字符串类型的参数,就会导致错误。所以要加上类型判断:

function getQueryString(url){
  if(typeof url == "string"){
    var pos = url.indexOf("?");
    if (pos > -1){
      return url.substring(pos + 1);
    }
  }
  return "";
}

还有,如果在流控制语句中使用非布尔值也会导致数据类型错误:

function reverseSort(values){
  if (values){//非数组值都会报错
    values.sort();
    values.reverse();
  }
}

另一种错误是将参数与 null 进行比较,这只能确保参数不是 null 和 undefined,也不建议将参数与 undefined 进行比较。

还有一种错误是,只针对某一特性进行检测:

function reverseSort(values){
  if (typeof values.sort == "function"){
    values.sort();
    values.reverse();
  }
}

上面的函数,如果传入带有 sort() 方法的对象,就会通过检测,但会在调用 reverse() 方法时报错!所以这里最好是使用 instanceof 来检测:

function reverseSort(values){
  if (values instanceof Array){
    values.sort();
    values.reverse();
  }
}

总的来说,基本类型的值使用 typeof 检测,而对象的值使用 instanceof 来检测。特别是面向公众的 API,一定要执行类型检查,以确保函数始终能够正常执行。

4.3 通信错误

JavaScript 与服务器之间的任何一次通信,就有可能会产生错误。

一种通信错误是发送了不正确的 URL 格式数据。一般是没有使用 encodeURIComponent() 对数据进行编码,可以使用这里的函数来处理查询字符串:

/**
 * 添加查询字符串
 * @param url URL
 * @param name 参数名
 * @param value 参数值
 * @returns {*}
 */
function addQueryStringArg(url, name, value) {
  if (url.indexOf("?") == -1) {
    url += "?";
  } else {
    url += "&";
  }
  url += encodeURIComponent(name) + "=" + encodeURIComponent(value);
  return url;
}

尽量使用这个函数,就可以确保编码正确。

5 区分致命错误与非致命错误

非致命错误可以根据以下列出的一个或多个条件来确定:

  • 不影响用户的主要任务;
  • 只影响页面的一部分;
  • 可以恢复;
  • 重复操作可以消除错误。

而致命错误也可以根据以下列出的一个或多个条件来确定:

  • 无法继续执行;
  • 影响到了用户的主要操作;
  • 导致其他连带的错误。

如果发生了致命错误,要立即发送消息通知用户,告诉他们无法再继续操作咯。如果必须刷新页面才会恢复,也必须通知用户,并提供了可刷新页面的按钮。

比如,在大型网站中,可能有多个互不依赖的模块,它们是类似这样初始化的:

for (var i=0, len=mods.length; i<len; i++){
  mods[i].init();
}

这样做的问题是,如果有一个模块发生错误,那么就会导致后续的其它模块无法被初始化,从而导致致命的错误!我们这样改造下,让致命的错误变成非致命的错误:

for (var i=0, len=mods.length; i<len; i++){
  try{
    mods[i].init();
  } catch (ex){
    ...
  }
}

6 把错误记录到服务器

建议集中保存错误日志,这样可以方便后续查找错误的原因。所以我们可以把 JavaScript 错误发送给服务器,让服务器保存起来。这样把前后端的错误集中起来管理,就可以很方便地对数据进行分析咯O(∩_∩)O~

首先先在服务器上创建一个接口,用于接收页面发送的错误数据:

/**
 * 记录 JavaScript 错误
 * @param sev 严重程度,比如:nonfatal
 * @param msg 错误消息
 */
function logError(sev,msg){
  var img=new Image();
  img.src="log.action?sev="+encodeURIComponent(sev)+"&msg="+encodeURIComponent(msg);
}

使用 Image 对象来发送请求,有这些好处:

  • 所有的浏览器都支持 Image 对象,包含那些不支持 XMLHttpRequest 对象的浏览器。
  • 可以避免跨域限制。比如可以使用一台日志服务器专门接收多台 web 服务器抛出的错误。

只要是使用了 try-catch 语句,就要把错误记录到日志中:

for (var i=0, len=mods.length; i<len; i++){
  try{
    mods[i].init();
  } catch (ex){
    logError("nonfatal","模块初始化失败:"+ex.message);
  }
}

第二个参数是上下文信息以及 JavaScript 的错误消息,带上上下文信息可以方便分析导致错误的真正原因。

希望本文所述对大家JavaScript程序设计有所帮助。

Javascript 相关文章推荐
用倒置滤镜把div倒置,再把table倒置。
Jul 31 Javascript
ExtJS 2.0实用简明教程 之Border区域布局
Apr 29 Javascript
jquery.validate使用攻略 第二部
Jul 01 Javascript
基于JQuery实现的类似购物商城的购物车
Dec 06 Javascript
代码分析jQuery四种静态方法使用
Jul 23 Javascript
jQuery平滑旋转幻灯片特效代码分享
Sep 07 Javascript
Javascript 计算字符串在localStorage中所占字节数
Oct 21 Javascript
浅谈js数组和splice的用法
Dec 04 Javascript
微信小程序canvas写字板效果及实例
Jun 15 Javascript
基于jquery实现左右上下移动效果
May 02 jQuery
小程序中使用css var变量(使js可以动态设置css样式属性)
Mar 31 Javascript
React服务端渲染原理解析与实践
Mar 04 Javascript
JSON基本语法及与JavaScript的异同实例分析
Jan 04 #Javascript
JavaScript解析及序列化JSON的方法实例分析
Jan 04 #Javascript
Node.js操作系统OS模块用法分析
Jan 04 #Javascript
Node.js console控制台简单用法分析
Jan 04 #Javascript
Node.js JSON模块用法实例分析
Jan 04 #Javascript
使用VUE+iView+.Net Core上传图片的方法示例
Jan 04 #Javascript
Node.js assert断言原理与用法分析
Jan 04 #Javascript
You might like
php简单实现无限分类树形列表的方法
2015/03/27 PHP
laravel添加前台跳转成功页面示例
2019/10/22 PHP
滚动经典最新话题[prototype框架]下编写
2006/10/03 Javascript
一个加密JavaScript的开源工具PACKER2.0.2
2006/11/04 Javascript
javascript FormatNumber函数实现方法
2008/12/30 Javascript
javascript学习笔记(七)利用javascript来创建和存储cookie
2011/04/08 Javascript
基于jQuery的模仿新浪微博时间的组件
2011/10/04 Javascript
jQuery bxCarousel实现图片滚动切换效果示例代码
2013/05/15 Javascript
JS获取几种URL地址的方法小结
2014/02/26 Javascript
jQuery表格列宽可拖拽改变且兼容firfox
2014/09/03 Javascript
node.js中的fs.lchmodSync方法使用说明
2014/12/16 Javascript
javascript中call apply 的应用场景
2015/04/16 Javascript
JS禁用页面上所有控件的实现方法(附demo源码下载)
2015/12/17 Javascript
jQuery循环遍历子节点并获取值的方法
2016/04/14 Javascript
50 个 jQuery 插件可将你的网站带到另外一个高度
2016/04/26 Javascript
jquery mobile 实现自定义confirm确认框效果的简单实例
2016/06/17 Javascript
深入理解javascript作用域第二篇之词法作用域和动态作用域
2016/07/24 Javascript
第一次接触神奇的前端框架vue.js
2016/12/01 Javascript
js实现数组去重方法及效率?Ρ? target=
2017/02/14 Javascript
详解Vue.js入门环境搭建
2017/03/17 Javascript
Angular 4依赖注入学习教程之FactoryProvider配置依赖对象(五)
2017/06/04 Javascript
利用require.js与angular搭建spa应用的方法实例
2017/07/19 Javascript
解决layui上传文件提示上传异常,实际文件已经上传成功的问题
2018/08/19 Javascript
JS XMLHttpRequest原理与使用方法深入详解
2020/04/30 Javascript
Python中使用logging模块代替print(logging简明指南)
2014/07/09 Python
pandas DataFrame创建方法的方式
2019/08/02 Python
python中for循环变量作用域及用法详解
2019/11/05 Python
通过代码简单了解django model序列化作用
2020/11/12 Python
Agoda台湾官网:国内外订房2折起
2018/03/20 全球购物
Swanson中国官网:美国斯旺森健康产品公司
2021/03/01 全球购物
生产车间主任的个人自我鉴定
2013/10/25 职场文书
大学生新闻专业个人自我评价
2013/11/12 职场文书
银行开业庆典方案
2014/02/06 职场文书
实习单位鉴定评语
2014/04/26 职场文书
法学院毕业生求职信
2014/06/25 职场文书
基于Python实现的购物商城管理系统
2021/04/27 Python