DDR爱好者之家 Design By 杰米

本文实例讲述了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.");

最常使用这些错误类型来创建自定义的错误消息:Error、RangeError、ReferenceError 和 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="/UploadFiles/2021-04-02/log.action">

使用 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错误与调试技巧总结》、《JavaScript传值操作技巧总结》、《javascript编码操作技巧总结》、《JavaScript中json操作技巧总结》、《JavaScript切换特效与技巧总结》、《JavaScript查找算法技巧总结》、《JavaScript动画特效与技巧汇总》、《JavaScript数据结构与算法技巧总结》、《JavaScript遍历算法与技巧总结》及《JavaScript数学运算用法总结》

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

DDR爱好者之家 Design By 杰米
广告合作:本站广告合作请联系QQ:858582 申请时备注:广告合作(否则不回)
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
DDR爱好者之家 Design By 杰米