发布于 2026-01-06 5 阅读
0

快乐的小意外 - 调试 Javascript

快乐的小意外 - 调试 Javascript

去年我在 HelsinkiJS 和 Turku ❤️ Frontend 聚会上做了题为“快乐的小意外——调试的艺术”的演讲(幻灯片)。

这周我花了很多时间调试一些奇怪的时区问题,突然想起了之前讨论过的话题。所以我想写一篇更详细、更侧重于 JavaScript 的文章,介绍一下不同的解决方案。

打印到控制台

以下所有示例都可以复制粘贴到开发者控制台中,然后开始尝试。

控制台日志

其中一项最被低估但功能强大的工具就是它console.log及其相关工具。它通常也是检查问题所在最简单快捷的第一步。

console.log(`The value of variable foo is ${foo}`)
Enter fullscreen mode Exit fullscreen mode

最简单的方法就是用可变值记录一些额外的文本。ES6 及其模板字面量让这一切变得轻而易举。无需担心字符串拼接的问题。

要打印多个值,您可以这样做

console.log(`name: ${name}, age: ${age}, city: ${city)`)
Enter fullscreen mode Exit fullscreen mode

但值得庆幸的是,ES6 为我们带来了对象属性值的简写形式。它允许我们创建对象,其中键将成为变量名,值将成为其值。

console.log({name, age, city})
Enter fullscreen mode Exit fullscreen mode

你甚至可以使用 CSS 为输出结果着色!

console.log(`%c This is blue,%c and this is green`, 'color: blue', 'color: green')
Enter fullscreen mode Exit fullscreen mode

控制台表格

很长一段时间以来,我使用控制台打印的方式仅限于上述几种。但最近我学到了很多新工具,这些工具对你来说可能也是新的。

let dog = {
  name: 'Bolt',
  age: 3,
  isGood: true
}

let cat = {
  name: 'Grumpy',
  age: 5,
  isGood: false
}

console.table([dog, cat])
Enter fullscreen mode Exit fullscreen mode

这样console.table就能得到一个清晰美观的表格数据视图。当对象共享相同的键时,这个功能尤其有用。

console.trace

如果您想检查函数调用的堆栈跟踪,可以使用console.trace()

function foo(bar) {
  console.trace();
  return bar * 2;
}

console.log(foo(21));

Enter fullscreen mode Exit fullscreen mode

控制台计数

有时你需要追踪某行代码的执行次数。添加新变量、追踪其执行次数并用 console.log 输出结果既繁琐,又可能导致代码中遗留未使用的变量。

function foo(bar) {
  if(bar > 0) {
    console.count('Bar was positive')
    return true;
  } else {
    console.count('Bar was 0 or negative')
    return false;
  }
}

foo(1)
foo(0)
foo(42)
foo(-210)
Enter fullscreen mode Exit fullscreen mode

控制台组

如果你的应用程序会输出大量内容,有时你可能需要更好地对它们进行分组。与其手动插入console.log('Here be dragons'),不如使用……console.group

function foo(bar) {
  console.group('At function body')
  console.log(`Executing function foo with a list of ${bar.length} items`)
  console.group('Inside loop')
  bar.forEach(baz => {
    console.log(`Value of baz is ${baz}`)
  })
  console.groupEnd()
  console.log(`Function foo has finished`)
  console.groupEnd()
}

foo([1,2,3,4])
foo(['Bolt', 'Lassie', 'Doggo'])
Enter fullscreen mode Exit fullscreen mode

调试器

JavaScript 的debugger关键字非常神奇。它能让你精准定位目标位置,并完全掌控局部和全局作用域。我们来看一个假设的例子,假设有一个 React 组件,它会接收一些 props。

const CardComponent = props => {
  debugger;
  return (
    <h1>{props.title}</h1>
    <ul>
      {props.items.map(item => (<li>{item}</li>))}
    </ul>
  );
}
Enter fullscreen mode Exit fullscreen mode

当组件渲染时,如果浏览器的开发者工具已打开,它会暂停执行并允许您访问控制台。在那里,您可以利用上一节中学到的知识,检查 props 或调试器执行范围内的任何其他变量的值。

我经常使用调试器作为快速原型工具:我跳转到代码中的特定位置,查看我有哪些数据以及数据的格式,然后在开发者工具控制台中构建接下来要实现的小部分内容,然后将这些内容移到代码本身中。

这种方法将反馈循环从“在编辑器中编写代码 -> 刷新浏览器”缩短为“在控制台中编写代码 -> 查看结果”。

进入调试模式后,您还可以逐行继续执行,以查看执行失败的位置。

浏览器开发者工具

如果你使用 console.log 和调试器,那么你可能已经熟悉浏览器开发者工具了。如果还不熟悉,你应该了解一下。不同浏览器的开发者工具略有不同,我最熟悉的是 Google Chrome,所以我的示例将以 Chrome 为例。如果你使用的是 Safari、Edge 或 Firefox,你可以通过一些探索或网络搜索轻松找到相关信息。

条件断点

您无需更改任何代码即可访问调试器状态。在SourcesChrome 开发者工具的标签页中,您可以打开一个 JavaScript 文件,然后单击任意行号,即可将其变为蓝色箭头。

更棒的是,你可以通过右键单击行号并选择“编辑断点”来使这些断点具有条件性。

虽然它指示你在此处提供一个布尔表达式,但你并非必须这样做。我使用这些断点条件语句来添加一个console.log在触发该断点条件时要运行的语句。它会返回 undefined,这是一个假值——它仍然会打印出来,但不会停止执行。

手表

Chrome 开发者工具的右侧是一系列按钮和面板。虽然它们都为您提供了有用的工具,但我只会重点介绍其中一个(您可以自行了解其他按钮和面板):监视。

在监视面板中,您可以添加变量,代码执行时,它会显示变量的当前值。这样,您无需在每个断点(例如循环中的断点)后跳转到控制台,只需查看监视面板中的值即可了解代码执行情况。

网络选项卡

在现代 Web 开发中,通过 HTTP 调用 API 端点是传输数据最常用的方式之一。Chrome 开发者工具的“网络”选项卡就是一个很好的例子,它展示了如何检查数据收发情况。

我在这里向https://api.pokemontcg.io/v1/cards?name=charizard发起了一个 API 调用,可以直接在“网络”选项卡中查看响应。这可以帮助您了解返回的数据以及其格式是否符合您的使用需求。

通过点击不同的标签页,您可以了解有关标头和响应以及调用性能的更多信息。

框架特定扩展

通过扩展程序对不同框架的支持非常出色。如果你正在使用ReactReduxVueAngular,那么绝对值得花时间了解一下这些扩展程序。

我在使用 React+Redux 应用时几乎一直都打开着 Redux 标签页,因为它能让我更好地了解有哪些数据可用。

安装该插件(并重启浏览器),您就可以在 Chrome 开发者工具中使用强大的扩展工具。

日志记录

虽然将信息打印到控制台很有用,但这仅在开发应用程序时有效。这些信息不会持久保存,也不会离开客户端浏览器的控制台,因此您无法查看问题历史记录。

这时日志服务就派上用场了。使用ElasticsearchBugSnag等工具,您可以将日志保存到服务中,从而获得分析数据并深入查找问题。当用户报告出现问题时,这一点尤其有用,您只需打开日志服务即可查看发生了什么。

将日志记录到这些服务非常简单。在 BugSnag 中,您可以使用其 SDK,每当发生值得记录的事情时,只需运行以下命令即可。

try {
  something.risky()
} catch (e) {
  bugsnagClient.notify(e)
}
Enter fullscreen mode Exit fullscreen mode

对于小型项目,您可以看看我朋友创建的Gentry 。

非技术方法

除了编写 Javascript 代码和与浏览器交互之外,还有一些非技术性的方法来调试软件。

橡皮鸭调试法

向同事解释你的目标、遇到的问题以及你已经尝试过的方法是一个很好的方法。这能迫使你从多个不同角度思考问题,并以一种往往能帮助你在同事回答之前找到解决方案的方式来提出问题。

为了避免同事们浪费时间听你滔滔不绝地讲个不停,你应该跟鸭子说话。我办公桌上就放着一只,虽然有人可能会觉得跟橡皮鸭说话很傻很疯狂,但这种方法真的有效。我的鸭子很特别,它是一只侦探鸭,大家都知道,侦探鸭对查出程序bug非常有效。

睡眠和散步(顺序不一定如此)

暂时放下电脑和问题,让你的潜意识去思考,换个角度想想其他事情。我通常每天下午散步20分钟,尤其是在遇到棘手问题的时候。睡觉也是个很好的方法,因为它可以让你的大脑摆脱杂念,理清思路,找到解决方案。

当我遇到特别棘手的问题时,我会这样做:

1)头脑风暴:把所有想法都写到笔记本上。问题是什么,在哪些情况下会发生,我尝试过什么,我的假设是什么。

2)出去散散步,放松放松,想想别的事情,享受新鲜空气和大自然。因为我已经把所有事情都记下来了,所以不用担心会忘记。

3)回到办公桌前。如果我在散步时对问题有了新的想法,我会把它们添加到笔记中。之后,继续解决问题。

休息听起来似乎有悖常理,但它能提升你的问题解决能力、工作效率和幸福感。当你不再拼命思考,而是让大脑放松一下时,你的幸福感也会随之提升。最好的解决方案并非强求而来,而是源于你给自己时间和空间去思考。

包起来

我们今天学到了什么?

console 是 JavaScript 中一个强大的工具。它最常用于 console.log,但了解它的各种功能和特性是值得的。

调试器是个好帮手。无论是作为跳转到特定状态以找出问题所在,还是作为交互式地帮助你构建下一段代码的工具,有效利用调试器都能提升你的工作效率。

花些时间学习浏览器的开发者工具。虽然你可以不用任何额外工具开发 React + Redux 应用,但一旦安装了相应的扩展,你就能更轻松地访问 Redux 状态、组件状态和 props,以及所有原本需要大量 console.log 和调试才能获取的信息。我之所以选择 Firefox 作为备用浏览器,完全是因为它内置了强大的 CSS Grid 工具。

如果你的应用还没有记录运行情况,那就赶紧开始记录吧。这能让你深入了解应用的运行状况和用户使用方式,还能帮助你在客户群起而攻之之前解决一些问题。

休息一下,跟鸭子聊聊天,散散步,睡睡觉。开发人员的工作量不应该(或者至少不应该)用敲键盘的小时数或编写的代码行数来衡量。我花很多时间不在键盘前:在笔记本上记笔记、画草图、设计、规划、散步、和同事讨论问题。

文章来源:https://dev.to/hamatti/happy-little-accidents---debugging-javascript-48ak