如何遍历你的代码

通过一次执行一行代码或一个函数,您可以观察数据和页面中的更改,来确切了解发生了什么。您还可以修改脚本中使用的数据值,甚至可以修改脚本本身。

为什么这个变量值为20而不是30?为什么这行代码似乎没有任何效果?为什么这个标志为真时应该是假的?每个开发人员都面临着这些问题,并逐步通过代码找出来。

设置断点后,返回页面并正常使用,直到运行到断点,将暂停页面上的所有JavaScript,焦点转移到DevTools 的Sources(源文件)面板,并突出显示断点。现在,您可以选择性地执行代码,并一步一步检查其数据。

TL;DR

  • 通过代码来观察问题发生之前或发生时,通过实时编辑测试更改。
  • 首选逐步调试,而不是控制台记录日志,因为记录的数据到达控制台的时刻,这个数据就已经过时了。
  • 启用Async call stack(异步调用堆栈)功能可以更好地了解异步函数的调用堆栈。
  • Blackbox脚本可以从调用堆栈中隐藏第三方代码。
  • 使用命名函数而不是匿名函数来提高调用堆栈的可读性。

执行逐步调试

所有步骤选项都通过侧边栏中的可点击图标表示 ,但也可以通过快捷方式触发。这里是总结:

图标
/
按钮
功能描述
image_8.pngResume恢复执行直到下一个断点。如果没有遇到断点,则恢复正常执行。
Long Resume恢复执行断点禁用500ms。(愚人码头注:所有的暂停都阻塞 500 毫秒后恢复)方便随时跳过断点,否则将继续暂停代码,例如循环内的断点。单击并按住Resume按钮,直到展开以显示该操作按钮。
Step Over执行下一行中发生的任何操作,并跳转到下一行。
Step Into如果下一行包含一个函数调用,Step Into将跳转到该函数并在该函数的第一行暂停。
Step Out执行当前函数的剩余部分,然后在函数调用后的下一个语句处暂停。
Deactivate breakpoints暂时禁用所有断点。 用于恢复完整的执行,而不实际删除你的断点。 再次单击可以重新激活断点。
Pause on exceptions当异常发生时自动暂停代码。

建议使用step into作为你典型的 “一次一行” 的控制执行操作,因为无论你进入和退出什么函数,它能确保一次只执行一条语句。

在怀疑未捕获异常导致问题时,使用Pause on exception(异常时暂停),但你不知道它在哪里。启用此选项时,您可以通过单击Pause On Caught Exceptions(捕获到异常时暂停)复选框来优化它;在这种情况下,只有在发生特定处理的异常时才暂停执行。

查看属性的作用域

当您暂停脚本时,Scope(作用域)窗格将显示那个时刻所有当前定义的属性。

该窗格在下面的截图中以蓝色突出显示。

仅当脚本暂停时,Scope(作用域)窗格才会有内容。当您的页面正在运行时,Scope(作用域)窗格显示空白。

Scope(作用域)窗格显示定义在本地(函数内部),闭包,和全球的属性。

如果属性旁边有一个剪头图标,则表示它是一个对象。单击剪头图标可以展开对象并查看其属性。

有时属性会变淡。 例如,下面截图中,属性constructorconfirm属性更淡。



颜色较暗的属性是可枚举的。颜色较淡的属性是不可枚举的。

调用堆栈

靠近边栏顶部的是Call Stack(调用堆栈)窗格。当代码在断点处暂停时,Call Stack(调用堆栈)窗格显示执行路径,按时间逆序,将代码带到该断点。这有助于理解现在执行到哪里,它是如何到达这里的,是调试的一个重要因素。

示例



index.html 文件中第50行的初始onclick事件,调用 dgjs.js JavaScript文件中的第18行 setone() 函数, 然后调用在同一个文件中第4行的 setall() 函数,执行的当前断点正是处于这个函数中。

启用异步调用堆栈

启用异步调用堆栈功能 ,进入执行你的异步函数调用,以获得更多的可见性。

  • 打开DevTools的Sources(源文件)面板。
  • Call Stack(调用堆栈)窗格上,勾选Async(异步)复选框。

下面的视频包含一个简单的脚本来演示异步调用堆栈的功能。在脚本中,使用第三方库来选择DOM元素。一个名为onClick的函数被注册为元素onclick的事件处理程序。每当调用onClick时,它又通过setTimeout调用一个名为f的函数,f的函数里面只是通过debugger关键字强制脚本暂停。



在视频中,触发断点,并展开Call Stack(调用堆栈)窗格。在堆栈中只有一个调用:f。然后启用异步调用堆栈功能,恢复执行脚本,断点再次被触发,然后第二次展开Call Stack(调用堆栈)窗格。这次,Call Stack(调用堆栈)窗格包含所有导致f的调用,包括第三方库调用和onClick的调用。第一次调用脚本时,调用堆栈中只有一个调用。第二次,有四个。简而言之,异步调用堆栈功能可提高异步函数完整调用堆栈的可见性。

提示:命名函数可以提高调用堆栈的可读性

匿名函数使得调用堆栈难以阅读。命名你的函数可以提高可读性。

下面两个截图中的代码段在功能上是等效的。这里代码的确切功能并不重要,重要的是第一个截图中的代码使用匿名函数,而第二个使用命名函数。

在第一个截图中的调用堆栈中,前两个函数都只是被标题为(anonymous function)(匿名函数)。在第二个截图中,前两个是命名的函数,这使得程序流一目了然,更容易理解。当您使用许多脚本文件时,包括第三方库和框架,并且你的调用堆栈有五或十个调用,当命名函数时,更容易理解调用堆栈流。

带匿名函数的调用堆栈:


4.png


带命名函数的调用堆栈:


5.png


把第三方代码放入Blackbox(黑箱)

把脚本文件放入Blackbox(黑箱),可以忽略来自第三方库的调用堆栈。

放入Blackbox(黑箱)前:


6.png


放入Blackbox(黑箱)后:


7.png


要把一个文件放入Blackbox(黑箱):

1、打开 DevTools Settings(设置)。


8.png


2、在左侧的导航菜单中,单击Blackboxing(黑箱)。


9.png


1、点击Add pattern...(添加模式)按钮。

2、在Pattern(模式)文本框输入您希望从调用堆栈中排除的文件名模式。DevTools 会排除该模式匹配的任何脚本。


10.png


3、在文本字段右侧的下拉菜单中,选择Blackbox(黑箱)以执行脚本文件但是排除来自调用堆栈的调用,或选择Disabled(禁用)来阻止文件执行。

4、点击 Add(添加) 保存。

下次运行页面并触发断点时,DevTools 将在Call Stack(调用堆栈)中隐藏任何来自放入黑盒脚本函数的调用。

数据操作

当代码执行暂停时,您可以观察和修改它正在处理的数据。当尝试跟踪变量时,这对看起来似乎有错误的值或传递的参数没有按预期收到时至关重要。

通过点击Show/Hide drawer(显示/隐藏抽屉式面板)  或按ESC键显示控制台抽屉式面板。 在打开的控制台同中,您可以:

  • 键入一个变量的名称,可以在当前函数的作用域中查看其当前值
  • 键入JavaScript赋值语句,可以更改值

尝试修改值,然后继续执行,看看它如何改变你的代码的结果,以及它是否符合您的预期。

示例


11.png


我们发现参数dow的当前值是2,但在恢复执行之前手动将其更改为3

实时编辑

观察和暂停执行代码有助于定位错误,并且实时编辑允许您快速预览更改,而无需重新加载。

要实时编辑脚本,只需在逐步调试时,单击Sources(源文件)面板的编辑器部分。就像在编辑器中进行更改一样,然后使用Ctrl+S(或者Cmd+SMac上)提交更改。此时,整个JS文件将被修改到VM中,并更新所有函数定义。

现在,您可以恢复执行;您修改的脚本将代替原来的执行,并且您可以观察到您的更改的效果。

示例

12.png

我们怀疑参数dow有问题,在任何情况下,它被传递给函数setone(),dow所接收的值是1时,那么它应该是0,收的值是2时,那么它应该是1,等等。 要快速测试递减的值是否证实存在这个问题,我们在函数的开头添加第17行代码,使用Ctrl+S(或者Cmd+SMac上)提交并恢复执行。

管理线程执行

使用Sources(源文件)面板上的Threads(线程)窗格暂停,step into(步入),并检查其他线程,例如service worker 或 web worker 线程。

为了演示Threads(线程)窗格,本节使用以下演示:Web Workers的基本示例

如果你在app上打开DevTools,你可以看到主脚本位于main.js中:

13.png

而 web worker 脚本 位于 worker.js 中:

14.png

主脚本监听 Multiply number 1(乘数1) 或 Multiply number 2(乘数2) 输入字段的更改。更改后,主脚本将向 web worker 发送一个消息,将两个数字相乘的值。web worker 执行乘法然后将结果传递回主脚本。

假设你在main.js中设置了一个断点,当第一个数字改变时被触发:


15.png



并且在worker.js中设置断点,并且当worker接收到消息时触发:


16.png


在APP UI界面上,修改第一个数字,就会触发两个断点。


17.png


Threads(线程)窗格中,蓝色箭头表示当前选择的线程。例如,在上面的截图中,main线程被选中。

DevTools中,所有用于逐步执行代码的控件(resumepause script exectionstep over next function callstep into next function call,等等)都适用于线程调试。换一种说法,如果点击Resume script execution(恢复脚本执行)按钮,而你的DevTools看起来像上面的截图,那么Main线程将恢复执行,但是 web worker 线程仍然会被暂停。Call Stack(调用堆栈)窗格和Scope(作用域)窗格也只显示Main线程的信息。

当你想要逐步调试 web worker 线程的代码时,或者查看其作用域和调用堆栈信息,只需在Threads(线程)窗格中单击 web worker 线程标签就可以了,使蓝色箭头在它旁边(表示选中)。下面的截图显示了调用堆栈和作用域信息在选择worker线程后如何更改。如果你再次按任何 逐步执行代码 按钮(恢复脚本执行,跳过下一个函数调用等),该操作将只适用于 worker 线程。Main线程不受影响。


19.png