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

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

14.4 调试技术

14.4.1 将消息记录到控制台

IE8、Firefox、Opera、Chrome 和 Safari 都有 JavaScript 控制台,可以用来查看 JavaScript 错误。而且,在这些浏览器中,都可以通过代码向控制台输出消息。对 Firefox 而言,需要安装 Firebug ,因为 Firefox 要使用 Firebug 的控制台。对IE8、Firefox、Chrome 和 Safari来说,则可以通过 console 对象向 JavaScript 控制台中写入消息,这个对象具有下列方法。

  • error(message): 将错误消息记录到控制台
  • info(message): 将信息性消息记录到控制台
  • log(message): 将一般消息记录到控制台
  • warn(message): 将警告消息记录到控制台
在 IE8、Firefox、Chrome 和 Safari 中,用来记录消息的方法不同,控制台中显示的错误消息也不一样。错误消息带有红色图标,而警告消息带有黄色图标。以下函数展示了使用控制台输出消息的一个示例:
function sum (num1, num2){
console.log("Entering sum(), arguments are " + num1 + "," + num2);
console.log("Before calculation");
var result = num1 + num2;
console.log("After calculation");
console.log("Exiting sum()");
return result;
}
不存在一种跨浏览器向 JavaScript 控制台写入消息的机制,但下面的函数倒可以作为统一的接口:
function log(message) {
if (typeof console == "object") {
console.log(message);
} else if (typeof opera == "object"){
opera.postError(message);
} else if (typeof java == "object" && typeof java.lang == "object"){
java.lang.System.out.println(message);
}
}
这个 log() 函数检测了哪个 JavaScript 控制台接口可用,然后使用相应的接口。可以在任何浏览器中安全地使用这个函数,不会导致任何错误,例如:
function sum(num1, num2){
log("Entering sum(), arguments are " + num1 + "," + num2);
log("Before calculation");
var result = num1 + num2;
log("After calculation");
log("Exiting sum()");
return result;
}
记录消息要比使用 alert() 函数强,因为警告框会阻断程序的执行,而在考察异步处理对时间的影响时,使用警告框会影响到结果。

14.5 常见的 IE 错误

多年以来,IE一直都是最难于调试 JavaScript 错误的浏览器。IE 给出的错误消息一般都很短又语焉不详,而且上下文信息也很少,有时甚至一点都没有。但作为用户最多的浏览器,如何看懂 IE 给出的错误也是最受关注的。下面几个小节将分别探讨一些在 IE 中难于调试的 JavaScript 错误。

14.5.1 操作终止

在 IE8 之前的版本中,存在一个相对于其他浏览器而言,最令人迷惑、讨厌,也最难于调试的错误:操作终止 (operation aborted)。在修改尚未加载完成的页面时,就会发生操作终止错误。发生错误时,会出现一个模态对话框,告诉你 "操作终止"。单击确定 (OK) 按钮,则卸载整个页面,继而显示一张空白屏幕;此时要进行调试非常困难。下面的示例将会导致操作终止错误:

<html><head><title>Operation Aborted Example</title></head><body><p>The following code should cause an Operation Aborted error in IE versions prior to 8.</p>    <div>    <script type="text/javascript">        document.body.appendChild(document.createElement("div"));        </script>    </div></body></html>

这个例子中存在的问题是:JavaScript 代码在页面尚未加载完毕时就要修改 document.body ,而且 <script> 元素还不是 <body> 元素的直接子元素。准确一点说,当 <script> 节点被包含在某个元素中,而且 JavaScript 代码又要使用 appendChild()、innerHTML 或其他 DOM 方法修改该元素的父元素或祖先元素时,将会发生操作终止错误 (因为只能修改已经加载完毕的元素)。

要避免这个问题,可以等到目标元素加载完毕后再对它进行操作,或者使用其他操作方法。例如,为 document.body 添加一个绝对定位在页面上的覆盖层,就是一种非常常见的操作。通常,开发人员都是使用 appendChild() 方法来添加这个元素的,但换成使用 insertBefore() 方法也很容易。因此,只要修改前面例子中的一行代码,就可以避免操作终止错误:

<html><head><title>Operation Aborted Example</title></head><body><p>The following code should not cause an Operation Aborted error in IE versions prior to 8.</p>    <div>    <script type="text/javascript">        document.body.insertBefore(document.createElement("div"), document.body.firstChild);        </script>    </div></body></html>


 

在这个例子中,新的 <div> 元素被添加到 document.body 的开头部分而不是末尾。因为完成这一操作所需的所有信息在脚本运行时都是已知的,所以这不会引发错误。

除了改变方法之外,还可以把 <script> 元素从包含元素中移出来,直接作为 <body> 的子元素。例如:

<html><head><title>Operation Aborted Example</title></head><body><p>The following code should not cause an Operation Aborted error in IE versions prior to 8.</p>    <div>    </div>    <script type="text/javascript">        document.body.appendChild(document.createElement("div"));    </script></body></html>


 

这一次也不会发生错误,因为脚本修改的是它的直接父元素,而不再是间接的祖先元素。

在同样的情况下,IE8不再抛出操作终止错误,而是抛出常规的 JavaScript 错误,带有如下错误消息:

HTML Parsing Error: Unable to modify the parent container element before the child element is closed (KB927917) .

不过,虽然浏览器抛出的错误不同,但解决方案仍然是一样的。

14.5.3 未找到成员

如前所述,IE中的所有 DOM 对象都是以 COM 对象,而非原生 JavaScript 对象的形式实现的。这会导致一些与垃圾收集相关的非常奇怪的行为。 IE 中的未找到成员 (Member not found) 错误,就是由于垃圾收集例程匹配错误 (the mismatched garbage collection routines) 所直接导致的。

具体来说,如果在对象被销毁之后,又给该对象赋值,就会导致未找到成员错误。而导致这个错误的,一定是 COM 对象。发生这个错误的最常见情形是使用 event 对象的时候。IE 中的 event 对象是 window 的属性,该对象在事件发生时创建,在最后一个事件处理程序执行完毕后销毁。假设你在一个闭包中使用了 event 对象,而该闭包不会立即执行,那么在将来调用它并给 event 的属性赋值时,就会导致未找到成员错误,如下面的例子所示:

document.onclick = function(){

var event = window.event;

setTimeout(function(){

event.returnValue = false;                         // 未找到成员错误

}, 1000);

};

在这段代码中,我们将一个单击事件处理程序指定给了文档。在事件处理程序中,window.event 被保存在 event 变量中。然后,传入 setTimeout() 中的闭包里又包含了 event 变量。当单击事件处理程序执行完毕后,event 对象就会被销毁,因而闭包中引用对象的成员就成了不存在的了。换句话说,由于不能在 COM 对象被销毁之后再给其成员赋值,在闭包中给 returnValue 赋值就会导致未找到成员错误。

14.5.4 未知运行时错误

当使用 innerHTML 或 outerHTML 以下列方式指定 HTML 时,就会发生未知运行时错误 (Unknown runtime error):一是把块元素插入到行内元素时,二是访问表格任意部分 (<table> 、 <tbody> ,等等) 的任意属性时。例如,从技术角度说,<span> 标签不能包含 <div> 之类的块级元素,因此下面的代码就会导致未知运行时错误:

span.innerHTML = "<div>Hi</div>";               // 这里,span 包含了 <div> 元素

在遇到把块级元素插入到不恰当位置的情况时,其他浏览器会尝试纠正并隐藏错误,而IE在这一点上反倒很较真儿。