在上一章,我们学习了如何在 Sources 面板中直接编辑 CSS 和 JavaScript。那种“改完即生效”的体验确实很爽,但它解决的是一个特定问题:快速验证修改方案。

但如果你面对的不是“改个颜色”或“调个数值”这种简单问题,而是“这段 JavaScript 逻辑为什么跑出来的结果不对”这种复杂问题呢?

这时候,光是“改代码”是不够的——你需要看代码是怎么跑的。

绝大多数初中级开发者遇到这种情况时的第一反应是:往代码里塞 console.log。在关键位置打印变量的值,刷新页面,看控制台输出,然后推测问题出在哪。

这个方法能用,但有两个明显的缺点:

第一,你每次都要改代码。 加 log、保存、刷新、看输出——如果发现漏掉了某个变量,再重复一遍。改完还得删,删完还得记得别提交到仓库。

第二,你只能看到“结果”,看不到“过程”。 console.log 告诉你某一时刻某个变量的值是什么,但它不会告诉你代码是怎么一步步走到这里的、每一步之间发生了什么变化。

断点(Breakpoint)就是为了解决这两个问题而存在的。

断点让你可以在代码的任意一行“踩下刹车” ——当 JavaScript 执行到这一行时,它会自动暂停。在暂停的那一刻,你可以看到所有变量的当前值、函数的调用栈、作用域中的每一个细节。然后你可以一步一步地往下走,观察代码的每一步执行。

不需要改代码,不需要加 log,不需要刷新——所有信息都在你眼前。

这一章,我们来学习断点的核心概念和基本用法。这是整个 JavaScript 调试中最重要的一环,也是从“会用 DevTools”到“精通调试”的关键一步。

小提示:这一章的内容非常重要,也偏操作。建议你打开一个包含 JavaScript 的页面,跟着文章一步步设置断点、触发暂停、观察变化。只有亲手操作过,才能真正理解断点的价值。

一、什么是断点?

用一句话概括:断点是你告诉浏览器“在这里停下来”的标记。

当你在 Sources 面板的某一行代码上设置了一个断点,浏览器在执行 JavaScript 时,会在执行到这一行之前自动暂停。就像你在读一本书时在某一页夹了一张书签——每次读到这一页,你都会停下来,仔细看看这一页写了什么,然后再决定是否继续往下读。

在代码暂停的那一刻,DevTools 会向你展示:

  • 所有变量的当前值——不仅是你关心的那几个,而是当前作用域中的所有变量。

  • 调用栈(Call Stack) ——当前这段代码是从哪里被调用过来的,完整的调用链条是什么样的。

  • 作用域(Scope) ——当前可以访问的所有变量,按作用域层级组织。

你可以利用这些信息,精确地了解代码在那一刻的状态,然后决定下一步怎么走——是继续执行,还是进入某个函数内部看看,还是跳过某段不相关的代码。

断点 vs console.log:

  • console.log 调试像是在黑夜中用手电筒照一下某个角落——你只能看到那一瞬间、你指定的那几样东西。

  • 断点调试像是打开了一盏灯——整个房间都亮了,你可以看到一切。而且你不需要在代码里留下任何痕迹。

二、行断点

行断点是最基础、最常用的断点类型。当你确切知道要在哪一行代码停下来时,就用行断点。

如何设置行断点

操作非常简单:

  1. 在 Sources 面板中,打开包含目标代码的文件。

  2. 找到你想要暂停的那一行代码。

  3. 点击该行行号区域。

设置成功后,行号上会出现一个蓝色的图标,表示这一行已经设置了断点。

小建议:如果你不确定断点有没有设置成功,可以看行号左侧——有蓝色标记就说明成功了。也可以在右侧的 Breakpoints 面板中确认。

断点什么时候触发?

断点会在代码执行到该行之前触发。

举个例子,假设你有这样一段代码:

function calculateSum(a, b) {
    const result = a + b;      // 第 2 行
    console.log(result);       // 第 3 行
    return result;             // 第 4 行
}

calculateSum(5, 3);

如果你在第 2 行设置了断点,当 calculateSum(5, 3) 被调用时,代码会在执行 const result = a + b 之前暂停。此时 a 和 b 的值已经传进来了(分别是 5 和 3),但 result 还没有被计算。

暂停之后,你可以在右侧的 Scope 面板中看到 a = 5、b = 3,然后你可以按 F10 单步执行,观察 result 被赋值为 8 的过程。

如何移除行断点?
  • 临时禁用:在 Breakpoints 面板中,取消勾选该断点前面的复选框。

  • 永久删除:再次点击行号左侧的蓝色图标,或者右键点击断点选择“Remove breakpoint”。

三、条件断点

行断点很好用,但有一个场景它处理得不够好:循环。

想象一下,你有一个循环要执行 1000 次,但问题只出现在第 999 次迭代。如果用普通行断点,代码会在每一次迭代都暂停——你得手动按 999 次“继续”才能到达目标位置。

条件断点解决了这个问题。它只在满足你指定的条件时才会触发。

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

  2. 右键点击行号。

  3. 选择 “Add conditional breakpoint”。

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

  5. 按 Enter 确认。

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

一个实际的例子

假设你有这样一段代码:

const users = [
    { id: 1, name: 'User 1' },
    { id: 2, name: 'User 2' },
    { id: 3, name: 'User 3' },
    // ... 一共 1000 个用户
];

for (let i = 0; i < users.length; i++) {
    processUser(users[i]);    // 你想在这里暂停,但只想看 id 为 999 的用户
}

你怀疑 id 为 999 的用户在 processUser 中出了问题。但你不想在每次循环都暂停。

在 processUser(users[i]); 所在行右键点击行号,选择“Add conditional breakpoint”,输入:

users[i].id === 999

然后按 Enter。现在,只有当 users[i].id 等于 999 时,代码才会在这一行暂停。之前的所有迭代都会正常执行,不会打断你。

小建议:条件断点特别适合调试循环、处理大型数据集、或者排查只在特定输入下才出现的 bug。学会使用它,能帮你省下大量“手动跳过无关迭代”的时间。

四、日志点

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

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

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

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

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

  2. 右键点击行号。

  3. 选择 “Add logpoint”。

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

  5. 按 Enter 确认。

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

日志点的语法

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

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

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

  • 对象字面量 { }:仅支持单变量(如 {user}),打印为键值对对象;不支持内部写运算符(如 {a+b} 会报错)。

  • 模板字符串:使用反引号(`),通过 ${} 嵌入变量或表达式,适合拼接文本(如 `余额${a}`)。

  • 加号拼接(+):使用加号连接字符串与变量,字符串部分需加引号。

  • 逗号分隔(,):使用逗号分隔字符串与变量,字符串部分需加引号。

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

举个例子:

“用户 ID:”+ {user.id} +“ 姓名:”+ {user.name}

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

用户 ID: 999 姓名: User 999
什么时候用日志点?
  • 你不想让代码暂停,但又想查看某些变量的值。

  • 你想在不修改源代码的情况下,在关键位置“埋点”输出信息。

  • 你需要在循环中观察每次迭代的变量变化,但不想每次都手动点击“继续”。

小建议:日志点和我们在第四章学过的在编辑器中直接修改 JavaScript 是两种不同的思路。修改代码适合“验证修复方案”,日志点适合“观察运行过程”。两者可以配合使用。

五、在代码中使用 debugger 语句

除了在 DevTools 界面中点击行号设置断点,你还可以直接在 JavaScript 代码中写 debugger; 语句。

function calculateSum(a, b) {
    const result = a + b;
    debugger;        // 执行到这里会自动暂停
    console.log(result);
    return result;
}

当浏览器执行到 debugger; 这一行时,如果 DevTools 是打开的,代码会自动暂停——效果和在那一行设置了一个行断点完全一样。

debugger 和行断点的区别

维度

行断点

debugger 语句

设置位置

DevTools Source 代码编辑区

源代码中

是否需要修改代码

不需要

需要

调试完后是否需要清理

关闭 DevTools 或删除断点即可

需要从代码中删除

适用场景

临时调试

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

小建议:debugger; 语句在开发阶段非常方便——你可以在关键位置“埋”好断点,每次打开 DevTools 都会自动暂停。但记得在提交代码前删除所有 debugger; 语句,否则可能会影响其他开发者的调试体验,甚至在生产环境中造成意外暂停。

六、管理断点

当你设置了多个断点之后,需要一个地方来统一管理它们。这个工具就是Source 面板右侧调试区(Breakpoints 面板)。

Breakpoints 面板能做什么?
  • 查看所有断点:Breakpoints 面板列出了你设置的所有断点,按文件分组、按行号排序。

  • 快速跳转:点击任意一个断点,编辑器会自动跳转到对应的文件和行号。

  • 临时禁用:取消勾选某个断点前面的复选框,这个断点就会被临时“关闭”——代码执行到这一行时不会暂停,但断点本身没有被删除,随时可以重新启用。

  • 永久删除:右键点击断点,选择“Remove breakpoint”。

  • 编辑条件:如果这是一个条件断点,你可以右键断点,选择“Edit breakpoint”来修改条件表达式。

小建议:在调试复杂问题时,你可能会设置很多断点。善用 Breakpoints 面板的“禁用”功能——暂时不用的断点先关掉,需要时再打开,比删了重建要快得多。

七、断点类型一览

行断点是最常用的,但 DevTools 还支持其他几种断点类型,适用于不同的调试场景。

断点类型

何时使用

如何设置

行断点

知道要在哪一行暂停

点击行号

条件断点

只在满足特定条件时暂停

右键行号 → Add conditional breakpoint

日志点

只输出日志,不暂停

右键行号 → Add logpoint

DOM 断点

DOM 节点被修改或删除时暂停

Elements 面板 → 右键节点 → Break on

事件监听器断点

特定事件(如 click)触发时暂停

breakpoints面板 → Event Listener Breakpoints

XHR/ Fetch 断点

网络请求 URL 匹配特定模式时暂停

breakpoints面板 → XHR/Fetch Breakpoints

异常断点

代码抛出异常时暂停

breakpoints面板 → Breakpoints

函数断点

特定函数被调用时暂停

debugger语句;行断点;console debug()

对于初中级开发者来说,行断点、条件断点和日志点是日常调试中最常用的三种。其他类型(如 DOM 断点、事件监听器断点)在你遇到特定场景时会非常有用——比如调试点击事件不响应、或者排查 DOM 被意外修改的问题。

小建议:先熟练掌握行断点和条件断点。等你用熟了,再逐步探索其他类型。不需要一次性全部掌握。

八、示例

让我们通过两个完整的例子,把这一章学到的内容串起来。

示例一:行断点、条件断点、日志点

你有一个计算两个数之和的页面。用户在两个输入框中分别输入 5 和 1,点击按钮后,页面显示 5 + 1 = 51。显然,结果应该是 6,但页面算出了 51。

调试步骤

第一步:打开 Sources 面板,找到相关的 JavaScript 文件

按 F12 打开 DevTools,切换到 Sources 面板。在 Page 标签页中找到包含按钮点击事件处理函数的 JavaScript 文件。

第二步:设置断点

你怀疑问题出在计算和更新显示的那几行代码上。找到类似这样的代码:

function updateLabel() {
    var addend1 = getNumber1();
    var addend2 = getNumber2();
    var sum = addend1 + addend2;
    label.textContent = addend1 + ' + ' + addend2 + ' = ' + sum;
}

在 var sum = addend1 + addend2; 这一行的行号上点击,设置一个行断点。

第三步:触发断点

回到页面,在输入框中输入 5 和 1,点击按钮。代码执行到断点处时自动暂停。

第四步:检查变量

在暂停状态下,查看右侧的 Scope 面板。你会看到:

  • addend1 的值是 "5"(字符串)。

  • addend2 的值是 "1"(字符串)。

问题找到了! getNumber1() 和 getNumber2() 返回的是字符串,而不是数字。"5" + "1" 在 JavaScript 中是字符串拼接,结果是 "51"。

第五步:验证修复

你可以在控制台中临时验证:输入 Number("5") + Number("1"),结果是 6。确认了修复方案——把字符串转换成数字再相加。

第六步:继续执行或移除断点

按 F8 继续执行,或者移除断点,回到代码编辑器中去修复源文件。

整个过程中,你没有添加一行 console.log,没有刷新页面,所有信息都在断点暂停的那一刻清晰地呈现在你面前。

示例二:事件监听器断点、XHR/Fetch断点、异常断点、函数断点

演示代码:

<button id="btn">点击我(测试事件监听器断点)</button>
<button onclick="testFunctionBreakpoint()">测试函数断点</button>
<button onclick="testException()">测试异常断点</button>
<button onclick="testXHR()">测试XHR/Fetch断点</button>

document.getElementById('btn-event').addEventListener('click', () => console.log('事件触发'));

function testFunctionBreakpoint() {
    let sum = 0;
    for (let i = 0; i < 3; i++) sum += i;
    console.log('函数断点结果:', sum);
}

function testException() {
    try { let obj = null; obj.someMethod(); }
    catch (e) { console.log('捕获到异常'); }
}

function testXHR() {
    fetch('https://jsonplaceholder.typicode.com/posts/1')
        .then(res => res.json()).then(data => console.log(data));
}

1. 事件监听器断点 (Event Listener Breakpoints)

这种断点会在指定的 DOM 事件(如点击、键盘输入等)触发时暂停代码执行。

操作步骤:

  1. 在 DevTools 的 Sources 面板右侧的Breakpoints 面板中,找到 Event Listener Breakpoints(事件监听器断点)窗格。

  2. 展开 Mouse 类别,勾选 click 事件。

  3. 回到页面,点击第一个按钮“点击我”。

  4. 效果:代码会立即暂停在 addEventListener 的回调函数内部(即 console.log('按钮被点击了!') 这一行)。

2. XHR/Fetch 断点

当发送 XMLHttpRequest 或 Fetch 请求时,如果 URL 包含你指定的字符串,代码会暂停。

操作步骤:

  1. 在 DevTools 的 Sources 面板右侧的Breakpoints 面板中,点击选择 XHR/fetch Breakpoints 窗格。

  2. 再点击右侧出现的 + 号(鼠标移上去会出现:Add XHR/fetch breakpoint)。

  3. 输入 URL 包含的子字符串,例如输入 typicode(因为我们的请求地址包含这个单词),然后按回车。

  • 如果你留空不输入,则会拦截所有的 XHR/Fetch 请求。

  1. 回到页面,点击“测试XHR/Fetch断点”按钮。

  2. 效果:代码会在 fetch 函数执行的地方暂停,让你可以查看请求的配置和参数。

3. 异常断点 (Exception Breakpoints)

当代码抛出错误(异常)时暂停,即使这个错误被 try...catch 捕获了也能暂停。

操作步骤:

  1. 在 Sources 面板右侧的Breakpoints 面板中,勾选 Breakpoints 窗格下面的 Pause on caught exceptions。

  2. 回到页面,点击“测试异常断点”按钮。

  1. 效果:代码会暂停在 obj.someMethod() 这一行,因为 obj 是 null,访问其属性会抛出 TypeError。此时你可以在 Scope 面板中查看当前的变量状态。

4. 函数断点 (Function Breakpoints)

有两种常用方式可以在函数执行时暂停:

方法一:行断点

  1. 在 Sources 面板左侧的文件导航器中,找到当前页面的 JS 文件。

  2. 找到 testFunctionBreakpoint 函数,点击该函数内部第一行代码(let sum = 0;)左侧的行号设置行断点。

  3. 行号上会出现一个蓝色标记,表示行断点已设置。

  4. 回到页面,点击“测试函数断点”按钮。

  5. 效果:函数执行到这一行时会暂停。

方法二:在 console 控制台中使用 debug() 函数

  1. 保持 Sources 面板打开,直接切换到 Console 面板(或按 Esc 打开底部抽屉并切换至 quick console面板)。

  2. 输入 debug(testFunctionBreakpoint) 并回车。

  3. 回到页面,点击“测试函数断点”按钮。

  4. 效果:只要 testFunctionBreakpoint 被调用,DevTools 就会自动在该函数的第一行暂停。

  • 取消调试可输入 undebug(testFunctionBreakpoint)。

  • 使用 debug() ,编辑器对应的行号不会出现蓝色标记。

小结与技巧

断点类型

设置位置

适用场景

行断点

Sources编辑区点击代码行号

代码执行到指定行时暂停

条件断点

Sources编辑区右键代码行号 Add conditional…

仅当满足特定条件时暂停

日志点

Sources编辑区右键代码行号 Add logpoint

不暂停代码,向控制台输出变量信息

事件监听器

Sources 右侧Breakpoints 面板

想知道某个点击/滚动/按键到底触发了什么代码

XHR/Fetch

Sources 右侧Breakpoints 面板

调试接口请求,查看发送前的数据或请求头

异常

Sources 右侧Breakpoints 面板

页面报错但不知道错在哪里,快速定位到抛错行

函数

代码行号 或 Console debug()

精确调试某个具体的业务逻辑函数

总结

在这一章中,我们学习了断点的核心概念和基本用法:

  • 什么是断点:在代码中设置的“停车点”,让 JavaScript 在执行到该行时自动暂停,方便你检查所有变量的值。

  • 行断点:最基础的断点类型,点击行号即可设置,在指定行暂停。

  • 条件断点:只在满足特定条件时才暂停,适合调试循环和大型数据集。

  • 日志点:不暂停代码,只在控制台输出消息,是 console.log 的无代码替代方案。

  • debugger; 语句:在代码中直接写 debugger;,效果和行断点一样。

  • 管理断点:通过右侧的 Breakpoints 面板查看、禁用、编辑和删除所有断点。

  • 断点 vs console.log:断点让你看到“全过程”和“所有变量”,而 console.log 只能看到你指定的那几样东西。

断点是 JavaScript 调试中最核心、最强大的工具。它把“盲人摸象”式的调试变成了“全景观察”——你不再需要猜测代码在做什么,而是可以亲眼看到它每一步的执行。

下一章预告:我们将学习代码暂停后的调试控制方法——如何单步执行、如何监视变量、如何分析调用栈,让你能够在暂停之后“指挥”代码一步一步地走。