快速入门:调试器导航 (JavaScript)
本快速入门演示如何在 Visual Studio 调试器中导航,以及如何在会话中查看程序状态。
本快速入门适用于不熟悉用 Visual Studio 进行调试的开发人员,以及要详细了解在 Visual Studio 调试会话中导航的开发人员。但其中不传授调试本身的技艺。示例代码中的函数仅为演示本主题中所述的调试过程。这些函数并未采用应用程序或函数设计的最佳实践。实际上,您将快速了解这些函数和应用程序本身,但并不深入研究任何内容。
本快速入门的各节旨在尽可能独立,以使您可跳过其中含有已熟悉信息的任意一节。同时,您也不需要创建示例应用程序。但是,我们建议这样做,并已使该过程尽可能简便易行。
**调试器键盘快捷键。**Visual Studio 调试器中的导航同时针对鼠标和键盘进行了优化。本主题中的许多步骤都加入了括号括起的键盘快捷键。例如,(键盘:F5)表示按 F5 键将开始或继续执行调试器。
备注
模块模式
Windows 应用商店应用程序经常使用 JavaScript“模块模式”将数据和函数封装在一个页面中。模块模式使用一个自执行的匿名闭包将页面功能与全局命名空间相分离。在本主题中,我们将此类函数称为“模块”。
在本主题中
可了解如何:
创建示例应用程序
设置断点并运行到该处,逐语句执行某个函数,然后检查程序数据
逐语句执行、逐过程执行和跳出函数
设置条件断点、运行到光标处和使变量可视化
在“局部变量”窗口中查看变量数据
查看对象的变量数据和原型链
检查作用域链数据
使用“调用堆栈”窗口导航至代码
创建示例应用程序
调试涉及到代码,因此,示例应用程序使用 Windows 应用商店应用程序的框架只是为了创建一个源文件,从中可了解在调试会话中导航的工作方式以及如何检查程序状态。将调用的所有代码均从 default.js 文件的 module 函数中进行调用。不添加控件,也不处理事件。
创建空白 JavaScript Windows 应用商店应用程序。打开 Visual Studio。在主页上,选择“新建项目”链接。在“新建项目”对话框中的“已安装”列表中选择“JavaScript”,然后选择**“Windows 应用商店”。在项目模板的列表中,选择“空白应用程序”**。随后 Visual Studio 将创建一个新的解决方案和项目,并在代码编辑器中显示 default.htm 文件。
注意加载到页面中的脚本文件。
base.js 和 ui.js 文件创建 Windows JavaScript 库。Windows JavaScript 库是一组 JavaScript 和 CSS 文件,通过这组文件,可更简便地创建使用 JavaScript 的 Windows 应用商店应用程序。请将它与 HTML、CSS 和 Windows 运行时配合使用以创建应用程序。
您的代码在 default.js 文件中开始。
**打开 default.js 源文件。**在解决方案资源管理器中,打开 js 节点,然后选择 default.js。
**将页面内容替换为示例代码。从 default.js 文件中删除所有内容。访问此链接:调试器导航示例代码 (JavaScript),然后将 JavaScript 部分中列出的代码复制到剪贴板。(在浏览器中选择“返回”**或帮助查看器返回本快速入门页。)在 Visual Studio 编辑器中,将这段代码粘贴到当前为空的 default.js 中。选择 Ctrl+ S 以保存文件。
现在即可实践本主题中的示例。
设置断点并运行到该处,逐语句执行某个函数,然后检查程序数据
最常用于启动调试会话的方法是从**“调试”菜单中选择“启动调试”**(键盘:F5)。随后应用程序启动,并持续执行至抵达某个断点、手动暂停执行、发生异常或应用程序结束为止。
在调试器中暂停执行后,可通过将鼠标暂停在某个活动变量上,在数据提示中查看该变量的值。
暂停应用程序的执行(也称为中断到调试器中)之后,可控制其余程序代码的执行方式。可继续逐行执行,同时从函数调用移至该函数本身,也可在一个步骤中执行一个被调用的函数。在逐句通过应用程序时调用这些过程。还可恢复应用程序的标准执行,并运行到已设置的下一个断点或运行到放置光标的行。可随时停止调试会话。调试器旨在执行必要的清理操作并退出执行。
示例 1
此示例中,在 default.js 的 module 函数体内设置一个断点,即该文件调用第一个用户语句。然后逐语句执行该函数,在调试器数据提示中查看变量值,再停止调试。
**设置断点。**在调用 app.start() 之后紧接执行的语句 callTrack = "module function"; 处设置断点。在源代码编辑器的阴影装订线中选择该行(键盘:将光标放置在该行上,然后选择 F9 键)。
随后在装订线中显示该断点图标。
运行到该断点。通过在“调试”菜单上选择“启动调试”(键盘:F5),启动调试会话。
应用程序开始执行,然后在紧接设置了断点的语句之前暂停执行。装订线中的当前行图标指出您所在的位置,并且当前语句为突出显示。
现在由您控制应用程序的执行,可在逐句通过各个程序语句时检查程序状态。
逐语句执行该函数。在“调试”菜单上,选择“逐语句”(键盘:F11)。
注意,调试器将移至下一行,即调用 example1 函数。再次选择**“逐语句”**。调试器将移至 example1 函数的第一行代码。尚未执行突出显示的行,但已在调用堆栈上加载了该函数,并分配了局部变量的内存。
当逐语句执行某行代码时,调试器执行以下操作之一:
如果下一个语句不调用您的解决方案中的函数,则调试器执行该语句,移至下一个语句,然后暂停执行。
如果该语句调用您的解决方案中的函数,则调试器将移至被调用函数的第一行,然后暂停执行。
继续逐语句执行 example1 的语句,直至抵达退出点。调试器突出显示该函数的右大括号。
**在数据提示中查看变量值。**继续逐语句执行 example1 的语句,直至抵达退出点。调试器突出显示该函数的右大括号。将鼠标暂停在变量名称上时,数据提示中显示该变量的名称和值。
添加针对 callTrack 变量的监视。本快速入门中全程使用 callTrack 变量展示在示例中调用的函数。若要更方便地查看该变量的值,请将其添加到某个监视窗口中。在编辑器中选择该变量名称,然后从快捷菜单中选择“添加监视”。
可在一个监视窗口中监视多个变量。只要暂停执行,被监视变量的值(如数据提示窗口中的值)就会更新。各个调试会话中均会保存您所监视的变量。
停止调试。在“调试”菜单上,选择“停止调试”(键盘:Shift+F5)。这样将结束调试会话。
逐语句执行、逐过程执行和跳出函数
与逐语句执行父函数调用的函数形成对照的是,逐过程执行某个函数将执行子函数,然后当父函数继续执行时,在调用函数中暂停执行。当熟悉某个函数的工作方式并确信执行它不会影响所调查的问题时,可逐过程执行该函数。
逐过程执行某行不包含函数调用的代码时,执行该行如同逐语句执行该行一样。
跳出子函数将继续执行该函数,然后在该函数返回其调用函数后暂停执行。确定某个较长函数的其余部分无关紧要时,可跳出该函数。
逐过程执行和跳出函数都会执行该函数。
示例 2
在此示例中,将逐语句执行、逐过程执行和跳出函数。
**在 module 函数中调用 example2 函数。**编辑 module 函数,将 var callTrack = "module function" 之后的一行替换为 example2();。
运行到该断点。通过在“调试”菜单上选择“启动调试”(键盘:F5),启动调试会话。调试器在断点处暂停执行。
逐过程执行该行代码。在“调试”菜单上,选择“逐过程”(键盘:F10)。调试器按照与逐语句执行 var callTrack = "module function" 语句相同的方式执行该语句。
**逐语句执行 example2 和 example2_a。**选择 F11 键以逐语句执行 example2 函数。继续逐语句执行 example2 的语句,直至抵达行 var x = example2_a();。同样,逐语句执行此行以移至 example2_a 的入口点。继续逐语句执行 example2_a 的每个语句,直至返回 example2。
**逐过程执行函数。**注意,example2 中的下一行 var y = example2_a(); 与上一行基本相同。可安全地逐过程执行此行。选择 F10 键,从 example2 的继续执行移至对 example2_a 的第二次调用。注意,callTrack 字符串指示 example2_a 函数执行了两次。
跳出函数。选择 F11 键以逐语句执行 example2_b 函数。注意,example2_b 与 example2_a 区别不大。若要跳出该函数,请在“调试”菜单上选择“跳出”(键盘:Shift+F11)。注意,callTrack 变量指示已执行 example2_b,并且调试器已返回 example2 继续执行的那一点。
停止调试。在“调试”菜单上,选择“停止调试”(键盘:Shift+F5)。这样将结束调试会话。
设置条件断点、运行到光标处和使变量可视化
条件断点指定一种导致调试器暂停执行的情况。该条件由任何可得出 true 或 false 的代码表达式指定。例如,可使用某个条件断点,仅当变量达到某个值时,才在频繁调用的函数中检查程序状态。
运行到光标处类似于设置一次性断点。暂停执行后,可在源代码中选择一行,然后继续执行,直至抵达所选行。例如,可逐句通过函数中的某个循环,然后确定该循环中的代码执行正确。可运行到放置在循环执行后的光标处,而不必逐句通过循环的每次迭代。
有时,在数据提示所在行或其他数据窗口中难以查看变量值。调试器可在文本可视化工具中显示字符串、HTML 和 XML,该工具在一个可滚动的窗口中呈现该值的格式化视图。
示例 3
此示例中设置一个条件断点,在某个循环的特定迭代处中断,然后运行到放置在该循环后的光标。还可在文本可视化工具中查看变量的值。
**在 module 函数中调用 example3 函数。**编辑 module 函数,将 var callTrack = "module function"; 之后的一行替换为行 example3();。
运行到该断点。通过在“调试”菜单上选择“启动调试”(键盘:F5),启动调试会话。调试器在 module 函数中的断点处暂停执行。
逐语句执行 example3 函数。在“调试”菜单上选择“逐语句”(键盘:F11)以移至 example3 函数的入口点。继续逐语句执行该函数,直至已迭代 for 块的一个或两个循环。注意,逐语句执行全部 1000 次迭代需要很长时间。
设置条件断点。在代码窗口的左侧装订线中,右击行 s += i.toString() + "\n";,然后选择快捷菜单上的“条件”。
选中**“条件”复选框,然后在文本框中键入 i == 500;。选择“为 true”选项,然后选择“确定”**。通过该断点,可在 for 循环的第 500 次迭代时检查该值。可通过白色十字识别条件断点图标。
运行到该断点。在“调试”菜单上,选择“继续”(键盘:F5)。在 i 上暂停以确认 i 的当前值为 500。另请注意,变量 s 表示为一行,比数据提示窗口长出许多。
**使字符串变量可视化。**单击 s 的数据提示中的放大镜图标。
随后将显示“文本可视化工具”窗口,而该字符串的值呈现为一个多行字符串。
运行到光标处。选择行 callTrack += "->example3";,然后选择快捷菜单上的“运行到光标处”(键盘:Ctrl+F10)。调试器完成循环迭代,然后在该行暂停执行。
停止调试。在“调试”菜单上,选择“停止调试”(键盘:Shift+F5)。这样将结束调试会话。
使用“运行到光标处”返回代码并删除断点
逐语句执行 Microsoft 或第三方提供的库代码时,“运行到光标处”很有用。虽然逐句通过库代码可提供有用的信息,但通常需要很长时间。此外,通常您对自己的代码更感兴趣。此练习展示如何这样做。
**在 app.start 调用处设置断点。**在 module 函数中的行 app.start() 处设置断点
运行到该断点,然后逐语句执行库函数。
逐语句执行 app.start() 时,编辑器将显示 base.js 中的代码。再逐语句执行几行。
**逐过程执行和跳出函数。**在逐过程执行 (F10) 和跳出 (SHIFT+F11) base.js 中的代码时,可能会得出结论,您并不想检查复杂冗长的启动函数。
将光标放置在您的代码中,并运行到该处。在代码编辑器中切换回 default.js 文件。选择 app.start() 后的第一行代码(不能运行到注释或空白行)。从快捷菜单中选择“运行到光标处”。调试器继续执行 app.start 函数,然后在该断点处暂停执行。
在“局部变量”窗口中查看变量数据
“局部变量”窗口是当前正在执行的函数的作用域链中参数和变量的一个树视图。
查看对象的变量数据和原型链
**向 module 函数添加一个数组对象。**编辑 module 函数,将 var callTrack = "module function" 之后的一行替换为 var myArray = new Array(1, 2, 3);
运行到该断点。通过在“调试”菜单上选择“启动调试”(键盘:F5),启动调试会话。调试器在断点处暂停执行。逐语句执行到该行。
打开“局部变量”窗口。在“调试”菜单上,指向“窗口”,然后选择**“局部变量”**。(键盘:Alt+4)。
检查 module 函数中的局部变量“局部变量”窗口显示当前正在执行的函数(module 函数)的变量作为树的顶级节点。输入某个函数后,JavaScript 将创建所有变量,并给出 undefined 作为这些变量的值。在该函数中定义的函数以其文本作为值。
**逐句通过 callTrack 和 myArray 定义。在“局部变量”窗口中找到 callTrack 和 myArray 变量。逐过程执行 (F10) 这两个定义,并注意“值”和“类型”**字段发生了变化。“局部变量”窗口突出显示自上次中断以来已更改的变量值。
**检查 myArray 对象。展开 myArray 变量。随后将列出该数组的每个元素。“[prototype]”**节点包含 Array 对象的继承层次结构。展开此节点。
**“方法”**节点列出 Array 对象的所有方法。
**“[prototype]”节点包含从其派生 Array 的 Object 对象的原型。“[prototype]”节点可递归。对象层次结构中的每个父对象都在其子对象的“[prototype]”**节点中有所描述。
停止调试。在“调试”菜单上,选择“停止调试”(键盘:Shift+F5)。这样将结束调试会话。
检查作用域链数据
函数的“作用域链”包括所有处于活动状态且该函数可访问的变量。全局变量是作用域链的一部分,如同定义当前正在执行的函数的函数中定义的任何对象(包括函数)也是作用域链的一部分一样。例如,在 module 函数中定义的任何函数均可访问在 default.js 的 module 函数中定义的 callTrack 变量。每个作用域在“局部变量”窗口中单独列出。
当前正在执行的函数的变量在该窗口的顶部列出。
作用域链中每个函数作用域的变量在该函数的**“[Scope]”**节点下列出。作用域函数按其顺序(从定义当前函数的函数到链中的最外侧函数)在链中列出。
**“[Globals]”**节点列出在任何函数之外定义的全局对象。
作用域链可能比较费解,因此最好通过示例进行阐述。在以下示例中,可了解 module 函数如何创建其自身的作用域,以及如何能够通过创建闭包来创建其他作用域级别。
示例 4
**从 module 函数中调用 example4 功能。**编辑 module 函数,将 var callTrack = "module function" 之后的一行替换为 example4():
运行到该断点。通过在“调试”菜单上选择“启动调试”(键盘:F5),启动调试会话。调试器在断点处暂停执行。
打开“局部变量”窗口。如有必要,请在“调试”菜单上,指向“窗口”,然后选择**“局部变量”。(键盘:Alt+4)。注意,该窗口列出 module 函数中的所有变量和函数,还包含“[Globals]”**节点。
**检查全局变量。展开“[Globals]”**节点。由 Windows JavaScript 库设置全局作用域中的对象和变量。可将您自己的变量添加到全局作用域。
**逐语句执行 example4 并检查其局部和作用域变量。逐语句执行(键盘:F11)example4 函数。由于 example4 是在 module 函数中定义的,因此 module 函数成为父作用域。example4 可调用 module 函数中的任意函数,并可访问其变量。展开“局部变量”窗口中的“[Scope]”**节点,并注意它包含 module 函数的相同对象和变量。
**逐语句执行 example4_a 并检查其局部和作用域变量。继续逐语句执行 example4 和对 example4_a 的调用。注意,局部变量现在来自 example4_a,而“[Scope]”**节点继续容纳 module 函数的变量。即使 example4 的变量处于活动状态,example4_a 也无法访问这些变量,并且这些变量不再是作用域链的一部分。
**逐语句执行 multipyByA 并检查其局部和作用域变量。**逐句通过 example4_a 的其余部分并逐语句执行 var x = multilpyByA(b); 行。
已将函数变量 multipyByA 设置为 multiplyClosure 函数,后者是一个“闭包”。multipyClosure 定义并返回一个内部函数 mulitplyXby,并捕获(覆盖)其参数和变量。在闭包中,返回的内部函数有权访问外部函数的数据,并这样创建其自身的作用域级别。
在逐语句执行 var x = multilpyByA(b); 时,移至 mulitplyXby 内部函数中的 return a * b; 行。
在“局部变量”窗口中,仅列出参数 b 作为 multiplyXby 中的局部变量,但已添加了一个新的**“[Scope]”级别。展开此节点,可看到其中包含 multiplyClosure 的参数、函数和变量,包括在 multiplyXby 的第一行调用的 a 变量。快速选中第二个“[Scope]”**节点可显示 module 函数变量,multiplyXby 在其下一行访问这些变量。
停止调试。在“调试”菜单上,选择“停止调试”(键盘:Shift+F5)。这样将结束调试会话。
使用“调用堆栈”窗口导航至代码
调用堆栈是一个数据结构,其中包含有关在应用程序当前线程中执行的函数的信息。命中断点后,“调用堆栈”窗口显示堆栈上所有活动函数的列表。当前正在执行的函数位于“调用堆栈”窗口列表的顶部。发起该线程的函数位于列表的底部。二者之间的函数显示从发起函数到当前函数的调用路径。
除了显示当前正在执行的函数的调用路径外,“调用堆栈”窗口还可用于在代码编辑器中导航至代码。当处理多个文件,并且要迅速移至某个特定函数时,这项功能很有用。
示例 5
在此示例中,逐语句执行一个调用路径,其中包含五个用户定义的函数。
**在 module 函数中调用 example5 函数。**编辑 module 函数,将 var callTrack = "module function"; 之后的一行替换为行 example5();。
运行到该断点。通过在“调试”菜单上选择“启动调试”(键盘:F5),启动调试会话。调试器在 module 函数中的断点处暂停执行。
打开“调用堆栈”窗口。在“调试”菜单中,选择“窗口”,然后选择**“调用堆栈”**(键盘:Alt+7)。注意,“调用堆栈”窗口显示两个函数:
**“全局代码”**是 module 函数的入口点,位于调用堆栈的底部。
**“匿名函数”**显示 module 函数中暂停执行的行。它位于调用堆栈的顶部。
逐语句执行各个函数以到达 example5_d 函数。在“调试”菜单上选择“逐语句”(键盘:F11),在调用路径中执行调用,直至抵达 example5_d 函数的入口点。注意,每次函数调用函数时,都会保存调用函数的行号,并将被调用函数放置在堆栈顶部。调用函数的行号就是调用函数暂停执行的点。黄色箭头指向当前正在执行的函数。
使用“调用堆栈”窗口导航至 example5_a 代码并设置断点。在“调用堆栈”窗口中,选择 example5_a 列表项,然后在快捷菜单上选择“转到源代码”。代码编辑器将光标设置在该函数的返回行处。在此行上设置断点。注意,未更改当前执行的行。仅移动了编辑器光标。
**逐语句执行函数,然后运行到该断点。**继续逐语句执行 example5_d。注意,从该函数返回时,将从堆栈中清除该函数。按 F5 以继续执行程序。将在上一步中创建的断点处停止。
停止调试。在“调试”菜单上,选择“停止调试”(键盘:Shift+F5)。这样将结束调试会话。