前面两章,我们学习了断点的基础用法和调试器的控制方法。你现在已经知道怎么在 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 代码就提交了”的风险。
如何设置日志点
在 Sources 面板中打开目标文件,找到你想要“埋点”的那一行。
右键点击行号。
选择 “Add logpoint”(添加日志点)。
在弹出的对话框中输入你想要输出的内容。
按 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。用几次你就会发现,这比改代码、刷新、再删代码的流程顺畅太多了。
三、条件断点:精准控制暂停时机
我们在第五章简单介绍过条件断点,但当时只是提了一下。这一节我们深入讲讲它的更多用法。
什么是条件断点?
条件断点是行断点的“升级版”——它只在满足你指定的条件时才暂停。
如何设置条件断点
在 Sources 面板中打开目标文件,找到目标行。
右键点击行号。
选择 “Add conditional breakpoint”。
在弹出的对话框中输入一个布尔表达式(返回 true 或 false 的表达式)。
按 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)——把常用的调试脚本保存下来,随时在任意页面上复用。