第14章 错误处理与调试 (二)

来源:互联网 发布:科讯cms漏洞 编辑:程序博客网 时间:2024/04/30 12:58
 

14.3 错误处理策略

过去,所谓 Web 应用程序的错误处理策略仅限于服务器端。在谈到错误与错误处理时,通常要考虑很多方面,涉及一些工具,例如记录和监控系统。这些工具的用途在于分析错误模式,追查错误原因,同时帮助确定错误会影响到多少用户。

在 Web 应用程序的 JavaScript 这一端,错误处理策略也同样重要。由于任何 JavaScript 错误都可能导致网页无法使用,因此搞清楚何时以及为什么发生错误至关重要。绝大多数 Web 应用程序的用户都不懂技术,遇到错误时很容易心烦意乱。有时候,他们可能会刷新页面以期解决问题,而有时候则会放弃努力。作为开发人员,必须要知道代码何时可能出错,会出什么错,同时还要有一个跟踪此类问题的系统。

14.3.1 常见的错误类型

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

  • 类型转换错误
  • 数据类型错误
  • 通信错误
以上错误分别会在特定的模式下或者没有对值进行足够的检查的情况下发生。
1.类型转换错误
类型转换错误发生在使用某个操作符,或者使用其他可能会自动转换值的数据类型的语言结构时。在使用相等 (==) 和不相等 (!=) 操作符,或者在 if、for 及 while 等流控制语句中使用非布尔值时,最常发生类型转换错误。
第3章讨论的相等和不相等操作符在执行比较之前会先转换不同类型的值。由于在非动态语言中,开发人员都使用相同的符号执行直观的比较,因此在 JavaScript 中往往也会以相同方式错误地使用它们。多数情况下,我们建议使用全等 (===) 和不全等 (!==) 操作符,以避免类型转换。来看一个例子:
alert(5 == "5");               // true
alert(5 === "5");             // false
alert(1 == true);             // true
alert(1 === true);           // false
这里使用了相等和全等操作符比较了数值 5 和字符串 "5" 。  相等操作符首先会将数值 5 转换成字符串 "5" ,然后再将其与另一个字符串 "5" 进行比较,结果是 true 。全等操作符知道要比较的是两种不同的数据类型,因而直接返回 false 。对于 1 和 true 也是如此:相等操作符认为它们相等,而全等操作符认为它们不相等。使用全等和非全等操作符,可以避免发生因为使用相等和不相等操作符引发的类型转换错误,因此我们强烈推荐使用。
容易发生类型转换错误的另一个地方,就是流控制语句。像 if 之类的语句在确定下一步操作之前,会自动把任何值转换成布尔值。尤其是 if 语句,如果使用不当,最容易出错。来看下面的例子:
function concat(str1, str2, str3){
var result = str1 + str2;
if (str3){                         // 绝对不要这样 !!!
result += str3;
}
return result;
}
这个函数的用意是拼接两或三个字符串,然后返回结果。其中,第三个字符串是可选的,因此必须要检查。第3章曾经介绍过,未使用过的命名变量会自动被赋予 undefined 值。而 undefined 值可以被转换成布尔值 false,因此这个函数中的 if 语句实际上只适用于提供了第三个参数情况。问题在于,并不是只有 undefined 才会被转换成 false,也不是只有字符串值才可以转换为 true 。例如,假设第三个参数是数值 0 ,那么 if 语句的测试就会失败,而对数值 1 的测试则会通过。
在流控制语句中使用非布尔值,是极为常见的一个错误来源。为避免此类错误,就要做到在条件比较时切实传入布尔值。实际上,执行某种形式的比较就可以达到这个目的。例如,我们可以将前面的函数重写如下:
function concat (str1, str2, str3){
var result = str1 + str2;
if(typeof str3 == "string"){               // 恰当的比较
result += str3;
}
return result;
}
在这个重写后的函数中,if 语句的条件会基于比较返回一个布尔值。这个函数相对可靠得多,不容易受非正常值的影响。
2.数据类型错误
JavaScript 是松散类型的,也就是说,在使用变量和函数参数之前,不会对它们进行比较以确保它们的数据类型正确。为了保证不会发生数据类型错误,只能依靠开发人员编写适当的数据类型检测代码。在将预料之外的值传递给函数的情况下,最容易发生数据类型错误。
在前面的例子中,通过检测第三个参数可以确保它是一个字符串,但是并没有检测另外两个参数。如果该函数必须要返回一个字符串,那么只要给它传入数值,忽略第三个参数,就可以轻易地导致它的执行结果错误。类似的情况也存在于下面这个函数中:
// 不安全的函数,任何非字符串值都会导致错误
function getQueryString(url){
var pos = url.indexOf("?");
if (pos > -1){
return url.substring(pos + 1);
}
return "";
}

这个函数的用意是返回给定 URL 中的查询字符串。为此,它首先使用 indexOf() 寻找字符串中的问号。如果找到了,利用 substring() 方法返回问号后面的所有字符串。这个例子中的两个函数操作字符串,因此只要传入其他数据类型的值就会导致错误。而添加一条简单的类型检测语句,就可以确保函数不那么容易出错:

// 不安全的函数,任何非字符串值都会导致错误

function getQueryString(url){

if (typeof url == "string"){                // 通过检查类型确保安全

var pos = url.indexOf("?");

if (pos > -1){

return url.substring(pos + 1);

}

}

return "";

}

重写后的这个函数首先检查了传入的值是不是字符串。这样,就确保了函数不会因为接收到非字符串而导致错误。

3.通信错误

随着 Ajax 编程的兴起,Web 应用程序在其生命周期内动态加载信息或功能,已经成为一件司空见惯的事。不过,JavaScript 与服务器之间的任何一次通信,都有可能产生错误。

第一种通信错误与格式不正确的 URL 或发送的数据有关。最常见的问题是在将数据发送给服务器之前,没有使用 encodeURIComponent() 对数据进行编码。例如,下面这个 URL 的格式就是不正确的:

http://www.yourdomain.com/?redir=http://www.someotherdomain.com?a=b&c=d

针对 "redir=" 后面的所有字符串调用 encodeURIComponent() 就可以解决这个问题,结果将产生如下字符串:

http://www.yourdomain.com/?redir=http%3A%2F%2Fwww.someotherdomian.com%3Fa%3Db%26c%3Dd

对于查询字符串,应该记住必须要使用 encodeURIComponent() 方法。为了确保这一点,有时候可以定义一个处理查询字符串的函数,例如:

function addQueryStringArg(url, name, value){

if(url.indexOf("?") == -1) {

url += "?";

} else {

url += "&";

}

url += encodeURIComponent(name) + "=" + encodeURIComponent(value);

return url;

}

这个函数接受3个参数:要追加查询字符串的URL、参数名和参数值。如果传入的 URL 不包含问号,还要给它添加问号;否则,就要添加一个和号,因为有问号就意味着有其他查询字符串。然后,再将经过编码的查询字符串的名和值添加到URL后面。可以像下面这样使用这个函数:

var url = "http://www.somedomain.com";

var newUrl = addQueryStringArg(url, "redir", "http://www.someotherdomain.com?a=b & c=d");

alert(newUrl);

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

另外,在服务器响应的数据不正确时,也会发生通信错误。第10章曾经讨论过动态加载脚本和动态加载样式,运用这两种技术都有可能遇到资源不可用的情况。在没有返回相应资源的情况下,Firefox、Chrome 和 Safari 会默默地失败,IE 和 Opera 则都会报错。然而,对于使用这两种技术产生的错误,很难判读和处理。在某些情况下,使用 Ajax 通信可以提供有关错误状态的更多信息。

原创粉丝点击