15.3 调试教程

在本教程中,我们将看到使用Firebug来调试一个充满了各类错误(语法错误、运行时错误和逻辑错误)的文件。这个页面只是一个简单的测验程序,它提出了3个问题并且显示出测试结果(在任何浏览器中打开chapter15目录下的complete_debugger.html文件,来看看这个页面如何像预期的那样工作)。

注意:参见1.3节中的“注意”部分以获取如何下载教程文件的信息。

可以使用带有“New watch expression……”的黄色工具栏来添加自己的变量和函数。只要单击一下黄色工具栏,就会弹出一个文本框。输入要跟踪的变量的名字,甚至是想要执行的一条JavaScript语句。例如,由于调试器不会跟踪for循环(参见本书3.3.3节)中的一个计数器变量,我们可以添加这个变量,并且在循环一步一步执行的过程中,可以看到每次迭代循环的时候这个计数器是如何改变的。

为了完成这个教程,我们需要运行Firefox Web浏览器,安装并打开Firebug扩展,参见15.2.1节的介绍。

1.启动Firefox并打开chapter15目录下的文件debugger.html。

在浏览器窗口的右上端,Firebug显示这里有一个错误(如图15-8所示)。你必须打开Firebug控制台,以弄清楚发生了什么错误。

2.单击Firefox右上角的Firebug图标以打开Firebug,并且打开控制台。

我们可以看到如图15-8所示的错误消息“Missing]after element list”。方括号用来创建数组(参见本书2.8.1节),因此,这条消息看上去是因为一个数组的结束方括号漏掉了。这个遗漏是一个语法错误(参见15.1.1节的“快速熟悉:错误类型”部分),因为它显示了代码中的一个“语法错误”(就像是一个句子漏掉了句号)。注意错误消息的右边,Firebug告诉我们这个错误发生在第15行。

15.3 调试教程 - 图1

图 15-8 Firebug控制台用于跟踪那些令一个脚本轰然倒塌的语法错误和运行时错误

3.启动文本编辑器并且打开文件debugger.html。找到第15行(该行上是一个单个的;)。在;前面输入一个结束方括号,如下所示:


];


这个方括号结束了一个嵌套数组(参见3.5节),该数组包含了测试的所有问题和答案。

4.保存文件,返回到Firefox并重新载入页面。

还有另一个错误!这次,错误控制台显示“$is not defined”并且指向包含jQuery$(document).ready()函数的第10行。当Firefox报告某些内容“not defined”(没有定义),这意味着代码引用了某些不存在的内容,这可能是还没有创建的一个变量或函数的名字。或者可能只是代码中的一个输入错误。在这个例子中,代码看上去很好。错误出现在页面的前面,在这段代码中:


<script src="_js/jquery-1.6.3.min.js"></script>


当操作外部脚本的时候,一个常见的错误是偶尔输错了到脚本的路径。在这个例子中,jquery-1.6.3min.js文件位于名为js的一个文件夹中,而该文件夹在页面所在的文件夹之外。这里的这段代码假设js文件应该和这个Web页面位于同一文件夹下,由于Firefox不能找到jquery-1.6.3.min.js文件(其中定义了jQuery的$()函数),因此,脚本产生一个错误。

5.把<script>标签修改为:


<script src="../_js/jquery-1.6.3.min.js"></script>


../表示js文件夹位于页面的文件夹之外,并且路径现在正确地指向了该jQuery文件。这个程序还有什么错呢?

6.保存文件,返回到Firefox并重新载入页面。

没有错误,看上去这个页面已经修改好了,不是吗?

7.单击Start Quiz按钮。

哦,又是一个错误。这次,控制台报告“askQuestions is not defined”并且指向脚本末尾的第70行附近。因为这个错误只是在程序运行的时候出现,它是一个运行时错误(参见本章“快速熟悉:错误类型”部分)。这个问题出现在脚本的末尾,在下面的条件语句中:


if(quiz.length>0){

askQuestions();

}else{

giveResults();

}


现在为止,如果我们发现某些内容没有定义的话,常常只是因为一个简单的输入错误。在这个例子中,askQuestions()是对一个函数的调用,因此,现在花点时间来检查代码并且尝试找到这个函数。

找到了吗?尽管没有askQuestions()函数,我们应该注意到,有一个askQuestion()(最后没有s)。

8.返回到文本编辑器,然后在第70行删除askQuestions()中最后的s(靠近脚本的末尾)。保存这个文件,在Firefox中重新载入它,然后,再次单击“Start Quiz”按钮。

现在,出现了带有5个多选项的测试题。遗憾的是,最后一个选项有一个“undefined”标签(label)。看上去像是一个错误。然而,Firebug控制台是空的,因此,技术上讲,这不是JavaScript错误。程序逻辑一定出现了某种错误。要探究这个问题的根本,我们需要使用Firebug调试器。

9.在Firebug中,单击Script标签页,并且从Script标签页下面的源代码菜单中选择debugger.html(如图15-9所示)。

这个Script标签页使我们能够访问页面的JavaScript。如果页面包含了JavaScript,并且我们链接到了其他的外部JavaScript文件,源代码菜单允许我们选择要调试哪一段JavaScript代码。

由于“undefined”单选按钮似乎位置不对,因此,创建该单选按钮的代码是开始查找这个bug的好地方。如果已经编写了这段脚本,我们应该能够知道到哪里去查看代码。然而,如果我们只是处理有错误的代码,可能必须找到这段脚本。

在这个例子中,单选按钮在一个名为buildAnswers()的函数内创建,该函数的作用是构建用单选按钮表示的一系列的多选项。这个函数接收一个数组,其中包含了一个列表,带有每个单选按钮的值。当执行完该函数,它返回包含了单选按钮的HTML的一个字符串。因此,这个函数是开始调试的好地方。

10.在Firebug的Script标签中,向下滚动直到看到第47行。单击第46行的左边来插入一个断点(如图15-9中的圆圈所示)。

在第46行的左边出现了一个红点。这个红点便是一个断点,或者说是代码中JavaScript停止运行该脚本的某个点。换句话说,当这段脚本再次运行,JavaScript解释器会在运行到这一行的时候停下来,从而我们能够一行一行地步进执行代码,看看背后到底发生了什么。

调试器还允许我们在程序运行的时候查看变量的值,这很像我们在本书15.2.3节所使用的console.log()函数。接下来,我们将告诉Firebug想要跟踪哪个变量。

15.3 调试教程 - 图2

图 15-9 在Firebug中,我们可以调试当前页面使用的任何脚本。源代码菜单允许我们选择嵌入到当前Web页面中的JavaScript,或者是来自任何添加的外部JavaScript文件的代码

这个步骤给Watch列表添加了变量i。这个变量在循环中用做一个计数器来记录循环运行了多少次(参见3.3.3节了解关于for循环的更多内容)。在脚本运行的时候,我们将能够看到这个值是如何变化。接下来,我们将添加要查看的另一个变量。

12.再次单击“New Watch Expression”栏,然后输入answers.length,然后按下回车键。

不要担心Firebug此时显示的值(它可能会显示“answers is not defined”)。直到真正地运行调试器并且运行到该函数的时候,才能够跟踪该变量的值。现在来看一下脚本的内部。

13.单击Firefox的Reload按钮或者按下Ctrl+R(æ-R)。当页面重新载入的时候,单击Web页面上的“Start Quiz”按钮。

这个脚本开始了,并且第一个问题显示到了Web页面上。但是,当创建单选按钮的时候,调试器停止在了第46行(参见图15-10的上图)。注意,在Watch标签中,i的值没有定义。这是因为在该行执行之前,断点就停止了该程序。换句话说,循环还没有开始,并且i变量还没有创建。

然而,answers.length的值设置为4。数组answers是传递给函数的一个数组,其中包含了答案(我们可以在Watch列表中看到数组元素列在列表下方)。数组的length属性表示数组中项目的数目,在这个例子中是4,因此,当该函数完成的时候,我们应该得到4个单选按钮。

14.单击Step Over按钮(如图15-10所示)。

11.在Firebug窗口的右边单击“New Watch Expression”栏,输入i,然后按下回车键。

这个按钮把我们带入到程序的下一行。现在,我们可以看到i设置为0。我们将继续单击执行该循环。

15.3 调试教程 - 图3

图 15-10 当我们使用Firebug步进执行程序的时候,行号右边的红色圆圈表示一个断点,而黄色箭头表示JavaScript解释器当前停止的行号。单击Step Over(或Step Into)来运行该代码行,并且停止在下一行

15.单击Step Over按钮,直到我们在Watch列表中看到i的值改变为5(如图15-10的下图所示)。

尽管answers数组只有4个项,我们可以看到for循环实际运行了5次(i的值)。因此,有趣的是该循环是如何停止的。别忘了,在for循环中,for语句中间的语句是一个条件,只有该条件为true,循环才能运行(参见3.3.3节)。在这个例子中,条件是i<=answers.length;。换句话说,该循环从包含0的i开始,并且只要i小于或等于answers数组中的项目数,就继续运行。换句话说,在循环执行之前,i将等于0、1、2、3和4,这等于运行了5次。然而,由于answers数组只有4项,第5次通过循环的时候,再没有可供显示的答案了,因此会显示“undefined”,因为answers数组中没有第5项了。

16.返回文本编辑器,将第46行的for循环修改为如下所示:


for(i=0;i<answers.length;i++){


现在,循环只是运行answers数组中的项目数那么多次,为每个可能的答案创建一个单选按钮。

17.保存文件,并在Firefox中预览。

可以通过在Firebug Script窗口中单击断点的红点来关闭断点,从而看到完成后的页面不受干扰地执行。

文件complete_debugger.html包含了本教程的完整版本。正如你所看到的,在一个程序中查找bug可能花费很多工作。但是,像Firebug这样的一个调试工具,使得我们看到程序内部的“机构”并且找到哪里出错变得更为容易。