阅读 5619

前端的水平线,错误处理和调试

本章内容

  • 理解浏览器报告的错误
  • 处理错误
  • 调试JavaScript代码

错误处理

错误处理在程序设计中的重要性是一定的。任何有影响力的Web应用程序都需要一套完善的错误处理机制。

try-catch语句

try {
    // 可能会导致错误的代码
} catch (e) {
    // 在错误发生时怎么处理
}
复制代码

错误类型

执行代码期间可能会发生的错误有多种类型。每种错误都有对应的错误类型,而当错误发生时,就会抛出相应类型的错误对象。
ECMA-262定义了下列7种错误类型:

  • Error

  • EvalError

  • RangeError

  • SyntaxError

  • ReferenceError

  • TypeError

  • URIError

  • RangeError类型的错误在数值超出相应范围时触发:

try {
    let items1 = new Array(-20);
var items2 = new Array(Number.MAX_VALUE);
} catch (e) {
    console.dir(e)
}
复制代码
  • 在找不到对象的情况下,会发生ReferenceError(这种情况下,会直接导致人所共知的“object expected”浏览器错误)。通常,在访问不存在的变量时,就会发生这种错误:
try {
    let obj = x;
} catch (e) {
    console.dir(e)
}
复制代码
  • SyntaxError类型, 当我们把语法错误的JavaScript字符串传入eval()函数时,就会导致此类错误。
eval('a++ b')
复制代码
  • TypeError类型在JavaScript中会经常用到,在变量中保存着意外的类型时,或者在访问不存在的方法时,都会导致这种错误。错误的原因虽然多种多样。**最常发生类型错误的情况,就是传递给函数的参数事先未经检查,结果传入类型与预期类型不相符。
Function.prototype.toString.call('name')   // 抛出typeError
复制代码
  • 在使用encodeURI()或decodeURI(),而URI格式不正确时,就会导致URIError错误。这种错误很少见,因为前面说的这两个函数的容错性非常高。

利用不同错误类型,可以获悉更多有关异常的信息,从而有助于对错误作出恰当的处理。

try {
    // ......
} catch (e) {
    if (e instanceof TypeError) {
        // 处理类型错误
    } else if (e instanceof ReferenceError) {
        // 处理引用错误
    } else {
        // 处理其他类型的错误
    }
}
复制代码

在跨浏览器编程中,检查错误类型是确定处理方式的最简便途径;包含在message属性中的错误消息会因浏览器而异。

合理使用try-catch

使用try-catch最适合处理那些我们无法控制的错误。假设你在使用一个大型JavaScript库中的函数,该函数可能会有意无意地抛出一些错误。由于我们不能修改这个库的源代码,所以大可将对该函数的调用放在try-catch语句当中。

抛出错误

与try-catch语句相配的还有一个throw操作符,用于随时抛出自定义错误。

throw new Error('Something bad happend')
复制代码

上面这行代码抛出一个通用错误,带有一条自定义错误消息。也可以像下面使用其他错误类型,也可以模拟出类似的浏览器错误。

throw new SyntaxError('I dont like your syntax');

throw new TypeError('What type of variable do you take me for?');

throw new RangeError('Sorry, you just dont have the range')

throw new EvalError('That doesnt evaluate.')

throw new URIError('Uri, is that you?');

throw new ReferenceError('You didnt cite your references properly');
复制代码

错误事件

window.onerror = function (message, url, line) {
    // 处理错误
}
复制代码

处理错误的策略

常见的错误类型

错误处理的核心,是首先要知道代码里会发生什么错误。由于JavaScript是松散类型的,而且也不验证函数的参数,因此错误只会在代码运行期间出现。一般需要关注三种错误:

  • 类型转换错误
  • 数据类型错误
  • 通信错误

类型转换错误

全等操作符知道要比较的是两种不同的数据类型,因而直接返回false。

数据类型错误

Javascript是松散类型的,也就是说,在使用变量和函数参数之前,不会对它们进行比较以确保它们的数据类型正确。

通信错误

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

  • 第一种通信错误与格式不正确的URL或发送的数据有关。最常见的问题是在将数据发送给服务器之前,没有使用encodeURIComponent()对数据进行编码。
        function addQueryString(url, name, value) {
            if (url.indexOf('?') === -1) {
                url += '?';
            } else {
                url += '&';
            }

            url += `&${encodeURIComponent(name)} = ${encodeURIComponent(value)}`;
            
            return url;
        }
复制代码

使用这个函数而不是手工构建URL,可以确保编码正确并避免相关错误。

区分致命错误和非致命错误

任何错误处理策略中最重要的一部分,就是确定错误是否致命。对于非致命错误,可以根据下列一或多个条件来确定:

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

把错误记录到服务器

开发Web应用程序中的一种常见的做法,就是集中保存错误日志,以便查找重要错误的原因。

要建立这样一种JavaScript错误记录系统,首先需要在服务器上创建一个页面,用于处理错误数据。这个页面的作用无非就是从查询字符中取得数据,然后再将数据写入错误日志中。这个页面可能会使用如下所示的函数:

function logError (lev, msg) {
    let img = new Image();
    img.src = `log.php?e`
}
复制代码

这个logError()函数接收两个参数:表示严重程度的数值或字符串及错误消息。其中使用了Image对象来发送请求,这样做非常灵活,主要表现如下几方面:

  • 所有浏览器都支持Image对象,包括那些不支持XMLHttpRequest对象。
  • 可以避免跨域限制。
  • 在记录错误的过程中出问题的概率比较低。大多数Ajax通信都是由JavaScript库提供的包装函数来处理的,如果库代码本身有问题,而你还在依赖该库记录错误,可想而知,错误消息是不可能得到记录的。

小结

错误处理至关重要。不能提前预测到可能发生的错误,不能提前采取恢复策略,可能导致较差的用户体验。

下面是几种避免浏览器响应JavaScript错误的方法。

  • 在可能发生错误的地方使用try-catch语句,这样你还有机会以适当的方式对错误给出响应。
  • 使用window.onerror事件处理程序,这种方式可以接受try-catch不能处理的所有错误。

另外,对任何Web应用程序都应该分析可能发生的错误。JavaScript中发生错误的主要原因如下。

  • 类型转换
  • 未充分检测数据类型
  • 发送给服务器或从服务器接收到的数据有错误