调试入门
学习实用的调试技巧——打印调试、读调用栈、用假设-验证循环系统地修 bug。
你将学到
- 调试是什么、背后的心态
- 如何用 console.log 检查值
- 如何读调用栈定位 bug
- 系统地修 bug 的假设-验证循环
概念讲解
调试入门
调试是发现并修复 bug 的过程。bug 是程序实际行为与你预期之间的任何差异。调试不是编程之外的事——它就是编程的大部分。擅长调试,才是高效的开发者。
调试心态
调试是侦探工作。你观察症状(错误输出、崩溃),倒推原因。关键态度:
- 好奇 —— "它为什么这样?"而不是"它应该行的啊!"
- 耐心 —— bug 往往微妙,急躁会改错地方。
- 证据优先 —— 用程序的真实行为验证你的猜测。
用 console.log 打印调试
最简单的技巧:在关键位置加 console.log 打印变量值。这能揭示程序*实际*在做什么。
function total(cart) {
console.log("cart:", cart); // 进来的是什么?
let sum = 0;
for (const item of cart) {
console.log("item:", item, "sum so far:", sum);
sum += item.price;
}
console.log("final sum:", sum);
return sum;
}
读调用栈
出错时,调用栈显示导致错误的函数调用链。从上往下读:第一帧是错误发生处,下面是调用方。这能精确定位位置。
假设-验证循环
- 稳定复现 bug。
- 提出假设 —— "我怀疑循环多跑了一次。"
- 验证 —— 加日志或改代码确认。
- 修复 —— 假设成立就做针对性修改。
- 确认 —— 验证修复有效且没破坏其他功能。
避免"瞎改碰运气"——每次修改都应验证一个具体假设。这把调试从沮丧的猜谜变成有条理的过程。
示例代码
// 有 bug 的函数:本应求平均值
function average(numbers) {
let sum = 0;
for (let i = 0; i <= numbers.length; i++) { // BUG: <= 应为 <
sum += numbers[i];
}
return sum / numbers.length;
}
// 调试:逐步打印
function averageDebug(numbers) {
let sum = 0;
for (let i = 0; i < numbers.length; i++) {
console.log(`i=${i}, value=${numbers[i]}, sum=${sum}`);
sum += numbers[i];
}
console.log(`final sum=${sum}, count=${numbers.length}`);
return sum / numbers.length;
}
console.log(averageDebug([10, 20, 30])); // 20 —— 正确!
第一版有差一错误(<= 而非 <),把 undefined 加进求和导致 NaN。调试版逐步打印,让边界错误一目了然。
动手试一试
- JSON Diff
对比是调试技巧:比较期望输出与实际输出,精确定位差异。
常见错误
错误写法
乱改代码指望 bug 自己消失。
正确做法
先提假设,再用日志或小改动验证。乱改常引入新 bug 却没修好原来的。
错误写法
把调试用的 console.log 留在成品代码里。
正确做法
上线前删掉或用调试开关包起来。残留日志会刷屏,甚至在生产环境泄露敏感数据。
相关资源
相关代码片段
- JavaScript Array Methods
理解内置方法能减少手写循环带来的 bug。
相关速查表
- JavaScript Cheatsheet
console 方法与错误处理语法的参考。
查阅不认识的术语
新手友好的术语表用大白话解释编程术语——变量、函数、DOM、Promise 等等。
动手做项目
通过引导式项目应用所学,含起始代码与解决方案。
解读错误信息
用大白话解释常见错误——什么意思、为什么发生、如何修复。