本章内容
- 理解浏览器报告的错误
- 处理错误
- 调试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中发生错误的主要原因如下。
- 类型转换
- 未充分检测数据类型
- 发送给服务器或从服务器接收到的数据有错误
原文作者:ZhangCheng