前面两章,我们学习了断点的基础用法和调试器的控制方法。你现在已经知道怎么在 Sources 面板里点击行号设置断点,也知道暂停之后怎么用 F10、F11 一步步跟踪代码了。

但你可能也感觉到了一个问题:每次都要在 Sources 面板里翻文件、找行号、点击设置断点——如果文件很深、代码很长,这个过程挺耗时间的。

有没有更快的办法?

答案是:debugger 命令。

你不需要打开 Sources 面板,不需要翻文件树,不需要找行号——只需要在代码里写一行 debugger;,浏览器执行到这一行时就会自动暂停。就像在代码里埋了一个“地雷”,踩到就停。

除了 debugger 命令,这一章我们还会学习两个高级断点技巧:

  • 日志点(Logpoint):不暂停代码,只在控制台输出信息——是 console.log 的“无代码”替代方案。

  • 条件断点:只在满足特定条件时才暂停——我们在第五章简单提过,这一章会深入讲解更多使用场景。

这三个技巧有一个共同点:它们都让你用更少的操作,完成更精准的调试。

小提示:这一章的内容偏“进阶技巧”,建议你先熟练掌握行断点和单步执行(第五、六章)之后再来看。当然,如果你已经有一定的调试经验,直接上手也没问题。

一、debugger 命令:在代码中埋下“暂停点”

什么是 debugger 命令?

debugger 是 JavaScript 中的一个内置语句。当浏览器在执行 JavaScript 时遇到 debugger; 这一行,它会自动暂停执行——效果和在那一行设置了一个行断点完全一样。

function calculate(a, b) {
    const sum = a + b;
    debugger;              // 执行到这里会自动暂停
    console.log('结果是:', sum);
    return sum;
}

当 calculate(5, 3) 被调用时,如果 DevTools 是打开的,代码会自动暂停——效果和在那一行设置了一个行断点完全一样。

debugger 和行断点的对比

维度

行断点

debugger 语句

设置位置

DevTools Source 代码编辑区

源代码中

是否需要修改代码

不需要

需要

调试完后是否需要清理

关闭 DevTools 或删除断点即可

需要从代码中删除

适用场景

临时调试

希望断点“伴随”代码存在

什么时候用 debugger?

场景一:快速在关键位置“埋点”

你正在写一段新代码,知道某个函数逻辑比较复杂,想在写完后就地埋一个断点。直接在函数里写一行 debugger;,比写完代码再跑到 Sources 面板里翻文件找行号要快得多。

场景二:在循环或条件分支中精准定位

你想在循环的第 5 次迭代时暂停,但条件断点写起来有点复杂。这时候可以在循环体内放一个 if 语句加 debugger;:

for (let i = 0; i < users.length; i++) {
    if (i === 5) {
        debugger;    // 只在第 5 次迭代时暂停
    }
    processUser(users[i]);
}

这种方式比条件断点更灵活——你可以用任何 JavaScript 逻辑来控制是否暂停。

场景三:在异步代码中定位问题

异步代码(如 setTimeout、Promise、async/await)的执行顺序有时候很难跟踪。在异步回调中放一个 debugger;,可以让你精确地在回调执行的那一刻暂停,查看当时的上下文。

fetch('/api/data')
    .then(response => response.json())
    .then(data => {
        debugger;    // 数据返回后立即暂停
        processData(data);
    });
重要提醒:记得删除 debugger;

debugger; 最大的风险是忘记删除。

如果你把一个 debugger; 留在了生产环境的代码里,而用户恰好打开了 DevTools(或者不小心按了 F12),代码就会暂停——用户可能会看到一个“卡住”的页面,体验极差。

最佳实践:

  • 只在开发阶段使用 debugger。

  • 提交代码前,全局搜索 debugger;,确保全部删除。

  • 如果你的项目使用构建工具(如 Webpack、Vite),可以考虑在 production 模式下自动移除 debugger; 语句。

小建议:在 VS Code 中,你可以在提交前用“搜索”功能全局搜一下 debugger;,确保没有遗漏。养成这个习惯,就不会把 debugger; 带到生产环境了。

二、日志点(Logpoint):不暂停、只输出

什么是日志点?

日志点(Logpoint)是 DevTools 提供的一个“断点变种”——它不暂停代码,而是在控制台输出一条消息。

你可能会想:“这不就是 console.log 吗?”

功能上确实类似,但有一个关键区别:日志点不需要修改源代码。

你在 DevTools 中添加日志点,代码文件本身没有任何变化。调试完了,直接删除日志点就行——不需要在代码里删 console.log,也不会有“忘记删除 debug 代码就提交了”的风险。

如何设置日志点
  1. 在 Sources 面板中打开目标文件,找到你想要“埋点”的那一行。

  2. 右键点击行号。

  3. 选择 “Add logpoint”(添加日志点)。

  4. 在弹出的对话框中输入你想要输出的内容。

  5. 按 Enter 确认。

设置成功后,行号上会出现一个紫色的图标。

日志点的输出语法

在 Chrome DevTools 的日志点里,你输入的永远是一个 JavaScript 表达式,DevTools 只是简单地把它放进 console.log() 里执行。

在日志点的对话框中,你可以输入:

  • 纯文本:需加引号(如 '文本'),否则会被当作代码执行而报错。

  • 变量名:直接输入变量名或表达式(如 a + b),多个值用逗号分隔。

  • {变量名}:对象简写 {变量名} 仅适用于单个已声明的变量,结果是一个对象,大括号内不能直接包含运算符或复杂表达式(如 {a + b} 会报语法错误,需改用模板字符串 ${a + b})。”

  • 模板字符串:使用反引号(`)定义模板字符串,并通过 ${变量名} 或 ${表达式} 嵌入动态内容(例如:`用户 ${user.id} 的余额是 ${user.balance}`)。

  • 加号拼接(+):使用加号连接字符串与变量,字符串部分需加引号(如 '用户 ' + user.id + ' 的余额是 ' + user.balance)。

  • 逗号分隔(,):使用逗号分隔字符串与变量,字符串部分需加引号(如 '用户 ' , user.id , ' 的余额是 ' , user.balance)。

在 Chrome DevTools 中,日志点的输入框不是一个自带魔法解析功能的模板,而是一个纯粹的 JavaScript 表达式执行环境。它最终会被直接塞进 console.log() 的括号里运行。

举个例子,假设你在一个循环中设置了日志点,输入:

`用户 ID: ${addend1 },姓名:${addend2}`

当代码执行到这一行时,控制台会输出类似:

用户 ID: 101,姓名: 烈阳
用户 ID: 102,姓名: 神愉
用户 ID: 103,姓名: 追风
什么时候用日志点?

场景一:在不暂停代码的情况下观察变量变化

有时候你只是想“看一眼”某个变量的值,不想让代码停下来打断流程。日志点是最合适的选择——它在后台默默输出,不影响页面运行。

场景二:在循环中追踪每次迭代

在循环中设置行断点会导致每次迭代都暂停,非常烦人。用日志点,你可以看到每次迭代的变量值,但代码不会停。

场景三:调试生产环境的问题

你不可能在生产代码里加 console.log 然后重新部署。但如果你有 source map,可以在生产环境的 Sources 面板中添加日志点——不修改代码,不重新部署,就能看到输出。

日志点 vs console.log

维度

console.log

日志点(Logpoint)

是否需要修改源代码

需要

不需要

调试完后是否需要清理

需要从代码中删除

关闭 DevTools 或删除日志点即可

能否在 production 环境使用

不能(需要重新部署)

可以(如果有 source map)

是否影响代码文件

是

否

小建议:从今天开始,当你需要“看一眼某个变量的值”时,先用日志点试试,而不是急着往代码里塞 console.log。用几次你就会发现,这比改代码、刷新、再删代码的流程顺畅太多了。

三、条件断点:精准控制暂停时机

我们在第五章简单介绍过条件断点,但当时只是提了一下。这一节我们深入讲讲它的更多用法。

什么是条件断点?

条件断点是行断点的“升级版”——它只在满足你指定的条件时才暂停。

如何设置条件断点
  1. 在 Sources 面板中打开目标文件,找到目标行。

  2. 右键点击行号。

  3. 选择 “Add conditional breakpoint”。

  4. 在弹出的对话框中输入一个布尔表达式(返回 true 或 false 的表达式)。

  5. 按 Enter 确认。

设置成功后,行号上会出现一个带有问号的橙色图标。

条件断点的典型场景

场景一:在循环中只关注某一次迭代

这是条件断点最经典的用法。你有一个循环要执行 1000 次,但问题只出现在第 999 次:

for (let i = 0; i < 1000; i++) {
    processItem(items[i]);    // 在这里设置条件断点:i === 999
}

设置条件 i === 999,代码只在最后一次迭代时暂停。前面 998 次正常执行,不会打扰你。

场景二:只关注特定数据

你有一个处理用户数据的函数,但只想在用户 ID 大于 1000 时暂停:

function handleUser(user) {
    // 在这里设置条件断点:user.id > 1000
    updateProfile(user);
    sendNotification(user);
}

场景三:追踪特定状态变化

你有一个状态变量,想知道它什么时候变成了某个特定值:

function updateState(newState) {
    state = newState;
    // 在这里设置条件断点:state === 'error'
    render(state);
}
条件断点的性能考虑

条件断点有一个小“代价”:每次代码执行到这一行时,DevTools 都要计算一次条件表达式。

在大多数情况下,这个开销可以忽略不计。但如果你在一个每秒执行上万次的热循环中使用条件断点,可能会感觉到页面变慢。这时候可以考虑用日志点替代,或者缩小条件的范围。

小建议:条件断点的表达式可以是任何合法的 JavaScript 表达式——你可以用 &&、||、! 组合多个条件,也可以调用函数(但要注意函数调用本身也有开销)。

四、三种技巧的对比与选择

到现在为止,我们介绍了三种高级断点的使用技巧。它们各有各的适用场景:

技巧

是否暂停

是否改代码

最佳场景

debugger;

暂停

需要改

快速埋点、临时调试

日志点(Logpoint)

不暂停

不需要

观察变量、追踪循环

条件断点

有条件暂停

不需要

只在特定条件下暂停

选择指南:

  • 你想让代码停下来,仔细检查所有变量 → 用行断点或debugger;。

  • 你想让代码停下来,但只在特定条件下 → 用条件断点。

  • 你不想让代码停下来,只想看一眼变量的值 → 用日志点。

  • 你想快速在代码里“埋”一个断点,不想去 Sources 面板里翻文件 → 用 debugger;。

小建议:这三种技巧不是互相排斥的——你完全可以在同一个调试过程中混合使用。比如先用 debugger; 快速定位问题区域,再用条件断点精确追踪,最后用日志点确认修复效果。

五、一个完整的综合示例

让我们用一个更复杂的例子,把这一章学到的所有技巧串起来。

场景

你有一个用户列表页面,需要处理 1000 个用户的数据。你发现第 888 个用户的处理结果不对劲,但前面的 887 个都是正常的。你需要在尽量不打断流程的情况下,找出问题所在。

调试步骤

第一步:用 debugger; 快速定位

你大概知道问题出在 processUser 函数里。在代码中加一行 debugger;:

function processUser(user) {
    debugger;    // 临时埋点
    // ... 处理逻辑
}

刷新页面,代码在 processUser 的第一行暂停。你确认了这个函数确实是被调用的,并且 user 对象的格式是正确的。

第二步:换成条件断点,精准定位

现在你已经确认 processUser 本身没问题,问题可能出在特定数据上。你不想每次都暂停,只想看第 888 个用户。

移除 debugger;,在 processUser 的第一行设置一个条件断点:

user.id === 888

刷新页面。代码正常执行了 887 次,然后在第 888 个用户时暂停。你可以在 Scope 面板中仔细检查这个用户的数据,发现 user.balance 是一个字符串而不是数字——问题找到了。

第三步:用日志点验证修复

你在代码中把 user.balance 转成了数字。现在你想确认修复是否对所有用户都生效,但不想在每个用户上都暂停。

移除条件断点,在 processUser 的关键位置设置一个日志点,输入:

`用户 ${user.id} 的余额是 ${user.balance},处理后是 ${processedBalance}`

刷新页面,控制台输出了所有 1000 个用户的处理结果。你快速扫了一眼,确认所有余额都变成了数字格式——问题解决了。

整个过程中,你只改了一行代码(debugger;),而且在验证完成后就删除了。其他所有调试操作都在 DevTools 中完成,没有污染源代码。

总结

在这一章中,我们学习了三种高级断点技巧:

debugger; 命令
  • 在代码中直接写 debugger;,执行到此处自动暂停。

  • 适合快速埋点、在循环或条件中精准控制暂停位置。

  • 记得在提交代码前删除所有 debugger;。

日志点(Logpoint)
  • 不暂停代码,只在控制台输出消息。

  • 支持用多种方式输出动态内容。

  • 适合观察变量变化、追踪循环、生产环境调试。

  • 不需要修改源代码,调试完直接删除。

条件断点
  • 只在满足指定条件时才暂停。

  • 适合在循环中只关注某一次迭代、或只关注特定数据。

  • 用右键点击行号 → “Add conditional breakpoint” 设置。

这三种技巧的共同点是:它们都让你用更少的操作完成更精准的调试。debugger; 让你在代码里快速埋点,日志点让你在不暂停的情况下观察数据,条件断点让你只在真正需要的时候才停下来。

从今天开始,试着在你的调试流程中加入这些技巧——先想清楚“我想做什么”(暂停?观察?精准定位?),再选择最合适的工具。

下一章预告:我们将学习代码段(Snippets)——把常用的调试脚本保存下来,随时在任意页面上复用。