像专业人士一样调试 JavaScript
探索 Chrome 提供的最佳调试工具
这篇文章最初由Giancarlo Buomprisco在Bits and Pieces上发表
当你的代码运行不符合预期时,你是否也会通过控制台日志来寻找解决方案?如果是,请继续阅读。
本文旨在向您解释 Chrome Dev Tools 提供的最有用的工具和技术,以便比以往更好、更快地调试您的 Javascript 代码。
读完本文后,您将能够比以前更好、更快地调试代码。
以下是我们将要经历的内容的摘要:
-
设置断点以在特定行调试代码
-
导航调用堆栈
-
暂停/恢复脚本执行
-
设置监视表达式
-
提高开发工具效率的技巧和窍门
在继续之前——一个免费提示:使用Bit ( Github ) 轻松管理、共享和重用你的 JS 组件。模块化和可重用性是代码更优质、更可持续的关键因素!
调试运行时代码
如果您调试代码的原因是由于错误或意外行为,您可能会对开发工具中的“源”选项卡感兴趣,我们将通过一系列各种场景深入探讨该部分。
断点
如果您正在阅读本文,您可能习惯于通过在控制台中打印某个值来调试特定行。但我想向您介绍一种比简单打印日志更强大、更深入代码的方法:断点。
设置断点通常是调试过程的第一步。大多数浏览器的内置开发工具都允许你在特定代码行处以及被调试页面上每行代码的特定语句处停止代码执行。但出于本文的目的,我们将专门使用 Chrome 开发者工具。
断点有什么用处?
通常,您可能希望停止代码的执行,以便您可以交互地检查我们感兴趣的特定上下文。
一旦代码在断点处停止,我们就可以通过访问范围、导航调用堆栈甚至在运行时更改代码来启动调试过程。
如何设置断点?
为了解释如何设置断点,我们将调试我用于教育目的的其中一个辅助项目的 Angular 代码库,尽管所使用的技术并不重要。
-
你可能要做的第一件事是打开开发工具并转到“Sources”选项卡
-
然后,我们需要打开要调试的文件。
提示:在 Mac 上,按 ⌘ + O 键会打开文件选择器,您可以在其中快速找到所需的文件。在 Windows 上,按 CTRL+O 键 -
打开文件后,我们最终可以通过单击想要停止的代码行来设置断点。

正如上图所示,我们可以比在某一行代码上设置断点更深入,我们还可以将其设置为同一行代码上的语句。
我们设置了3个断点:
-
在定义时停止执行的第一行代码
-
第二个将在 priceReceived 函数执行之前停止
-
第三个函数将在 priceReceived 被调用后立即停止,因此我们也可以检查箭头函数的返回值
当箭头函数被调用时,执行停止,右侧面板范围将填充有关当前上下文的信息,并允许我们访问所有范围,以便我们可以检查我们感兴趣的值。
在这种情况下,您可以看到我们如何查看变量价格的值。
在下图中,一旦函数priceReceived
执行完毕,我们的第三个断点就会被触发。
正如您在右侧面板中看到的,返回值将显示匿名函数返回的内容
暂时暂停断点
场景:您在整个代码库中设置了一堆断点。
您可能已经经历过,在调试时多次刷新页面是很常见的。
你正在调试的代码可能有各种断点,有时甚至会被调用数百次!是的,这确实很令人沮丧,也很耗时。
在这种情况下,我建议暂时暂停所有断点的执行,您可以通过切换下图中显示的图标来执行此操作:

发生错误时停止执行
场景:您遇到意外错误,但您不想设置断点,因为您不确定何时会抛出错误。
您可以在错误发生时立即停止执行,以便检查范围并了解出了什么问题。

条件断点
条件断点,顾名思义,允许我们仅在条件为真时触发某些断点。
例如,在上面的例子中,用户可以在文本区域输入非数字值。JS 对此非常宽容,只会显示 NaN 而不是抛出错误。
场景:您的代码比上面的代码更复杂,并且无法确定结果何时为 NaN。
当然,你可以设置断点,但重现错误并不容易,最终你得花半个小时单步执行代码。在这种情况下,你可以使用条件断点,只有当检查到的值为 NaN 时才中断执行。
参见下图:

-
我们右键单击要添加断点的代码行
-
点击“添加条件断点...”
-
添加一个有效的 JS 表达式。当然,当表达式被调用时,你可以访问作用域,这意味着我们可以引用参数 x 和 y
-
当表达式为真时,断点就会被触发!
单步执行代码
为了充分利用开发工具,值得花一点时间了解开发工具如何帮助我们快速执行代码而无需在每一行设置断点。
- Step是 Dev Tools 中最简单的导航器,它允许您根据执行顺序逐行执行代码。需要注意的是,Step是最近由于Step Into 下一个函数调用的变更而引入的。在调试异步代码时,Step将按时间顺序移动到下一行。

- 跳过下一个函数调用。此导航器允许您逐行执行,但不会跳过函数调用。也就是说,函数调用将被跳过,除非在其中设置了断点,否则调试器不会在该函数的语句中停止。

如果您注意上面的图片,会发现 multiplyBy 和 renderToDOM 已经被执行,但是调试器并没有像之前的导航器那样单步执行它们(Step)。
- 从 Chrome 68 开始,此导航器的行为有所改变。这与我们之前看到的 Step 类似。区别在于,当单步执行异步代码时,它会在异步代码中停止,而不是在按时间顺序运行的代码中停止。

观察上面的图片:按时间顺序,第 32 行应该运行,但它没有运行。调试器等待了 2 秒,然后移至第 29 行。
- *跳出函数调用 *假设您对某个函数的执行不感兴趣,此导航器允许您跳出该函数,并在函数调用后的下一行停止

上图中发生了什么?
-
我们在第 36 行的断点处停了下来
-
我们跳出了renderToDOM函数
-
调试器直接移动到第 29 行并跳过函数 renderToDOM 的其余部分
全局变量和即时求值
有时在全局范围内存储一些值(例如组件的类、巨大的数组或复杂的对象)会很有用。
当您想要使用不同的参数调用该组件上的方法时,在调试时将这些值添加到全局范围可以节省大量时间。

在上图中,我将数组 [previous, current] 保存为全局变量。开发者工具会自动为该变量分配一个名称,即 temp{n},其中 n 的值取决于之前保存的变量数量。
正如您在上图中所看到的,变量被命名为 temp2,我可以在控制台中使用它,因为它现在是全局定义的!
得益于Chrome 68 中发布的一项功能“Eager Evaluation ”,开发者工具允许在编写语句时在控制台中对其进行评估,并显示方法的签名。
如果您注意上面的图片,当我将保存的变量映射到字符串数组时,结果会立即可见,而无需按 Enter。
导航调用堆栈
导航调用堆栈是开发工具提供的最有用的工具之一:您不仅可以在调用堆栈中来回跳转,还可以检查每一步的范围。
假设我们有一个简单的页面和一个脚本,该脚本接受输入数字并在页面上呈现乘以 10 的数字。我们将调用两个函数:一个用于相乘,一个用于将结果呈现给 DOM。

正如您在上图中所看到的,我们只需单击“调用堆栈”窗格中的函数名称即可浏览调用堆栈。
您可能还注意到,每次我们从一个调用跳转到另一个调用时,范围都会保留,我们可以在每一步对其进行分析!
使用黑盒脚本来扁平化堆栈
黑盒脚本将通过从堆栈中排除某些脚本或符合特定模式的脚本来帮助整理调用堆栈。
例如,如果我只对调试用户空间代码感兴趣(这可能占 99% 的时间),我将添加一个模式来黑盒化文件夹 node_modules 下的所有脚本。
为了黑盒化脚本,你有两种方法:
-
右键单击“Sources”面板中的脚本,然后单击“Blackbox Script”
-
进入 Chrome 设置页面,然后进入黑盒模式 *并点击添加模式... *并输入您想要黑盒的模式,这在您想要排除大量脚本时很有用
监视表达式
监视表达式使您可以定义一些 JavaScript 表达式,开发工具会跟踪并执行这些表达式,并显示当前结果。这是一个非常有趣的工具,因为您几乎可以编写任何您想要的内容,只要它是有效的 JavaScript 表达式即可。
例如,您可以编写一个表达式并期望该表达式的结果始终为真,这样当表达式为假时,您就知道当前状态下出现了问题。
有一个问题:
-
当我们使用断点进行调试时,监视表达式将被实时评估,无需刷新
-
如果代码正在执行,则需要手动单击刷新按钮
最后的话
开发者工具是调试复杂代码的绝佳资源。有时,您可能需要比控制台日志记录更深入的了解,而上述工具将为您提供深入的调试体验。这些工具需要一些练习才能完全熟练地使用,所以如果您对所有可用的选项感到不熟悉,请不要灰心。
资源
以下是一些资源,可帮助您全面了解开发工具提供的所有可用选项:
如果您需要任何澄清,或者您认为某些内容不清楚或错误,请发表评论!
如果您喜欢这篇文章,请在Medium或Twitter上关注我,以获取有关 Javascript、RxJS、Typescript 等的更多文章!
鏂囩珷鏉ユ簮锛�https://dev.to/gc_psk/debugging-javascript-like-a-pro-2gc0