Skip to content

调试入门

学习实用的调试技巧——打印调试、读调用栈、用假设-验证循环系统地修 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;
}

读调用栈

出错时,调用栈显示导致错误的函数调用链。从上往下读:第一帧是错误发生处,下面是调用方。这能精确定位位置。

假设-验证循环

  1. 稳定复现 bug。
  2. 提出假设 —— "我怀疑循环多跑了一次。"
  3. 验证 —— 加日志或改代码确认。
  4. 修复 —— 假设成立就做针对性修改。
  5. 确认 —— 验证修复有效且没破坏其他功能。

避免"瞎改碰运气"——每次修改都应验证一个具体假设。这把调试从沮丧的猜谜变成有条理的过程。

示例代码

              // 有 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 留在成品代码里。

正确做法

上线前删掉或用调试开关包起来。残留日志会刷屏,甚至在生产环境泄露敏感数据。

相关代码片段

相关速查表

查阅不认识的术语

新手友好的术语表用大白话解释编程术语——变量、函数、DOM、Promise 等等。

查看术语表

动手做项目

通过引导式项目应用所学,含起始代码与解决方案。

查看项目

解读错误信息

用大白话解释常见错误——什么意思、为什么发生、如何修复。

查看错误