调试

当程序的运行结果不符合预期或在运行过程中产生错误时,你就会注意到程序出现问题了,下一步就是要推断问题出在什么地方。

有时错误很明显。错误消息会指出错误出现在程序的哪一行,只要稍加阅读错误描述及出错的那行代码,你一般就知道如何修正错误了。

但不总是这样。 有时触发问题的行,只是第一个地方,它以无效方式使用其他地方产生的奇怪的值。 如果你在前几章中已经解决了练习,你可能已经遇到过这种情况。

下面的示例代码尝试将一个整数转换成给定进制表示的字符串(十进制、二进制等),其原理是:不断循环取出最后一位数字,并将其除以基数(将最后一位数从数字中除去)。但该程序目前的输出表明程序中是存在bug的。

  1. function numberToString(n, base = 10) {
  2. let result = "", sign = "";
  3. if (n < 0) {
  4. sign = "-";
  5. n = -n;
  6. }
  7. do {
  8. result = String(n % base) + result;
  9. n /= base;
  10. } while (n > 0);
  11. return sign + result;
  12. }
  13. console.log(numberToString(13, 10));
  14. // → 1.5e-3231.3e-3221.3e-3211.3e-3201.3e-3191.3e-3181.3…

你可能已经发现程序运行结果不对了,不过先暂时装作不知道。我们知道程序运行出了问题,试图找出其原因。

这是一个地方,你必须抵制随机更改代码来查看它是否变得更好的冲动。 相反,要思考。 分析正在发生的事情,并提出为什么可能发生的理论。 然后,再做一些观察来检验这个理论 - 或者,如果你还没有理论,可以进一步观察来帮助你想出一个理论。

有目的地在程序中使用console.log来查看程序当前的运行状态,是一种不错的获取额外信息的方法。在本例中,我们希望n的值依次变为 13,1,然后是 0。让我们先在循环起始处输出n的值。

  1. 13
  2. 1.3
  3. 0.13
  4. 0.013
  5. 1.5e-323

没错。13 除以 10 并不会产生整数。我们不应该使用n/=base,而应该使用n=Math.floor(n/base),使数字“右移”,这才是我们实际想要的结果。

使用console.log来查看程序行为的替代方法,是使用浏览器的调试器(debugger)功能。 浏览器可以在代码的特定行上设置断点(breakpoint)。 当程序执行到带有断点的行时,它会暂停,并且你可以检查该点的绑定值。 我不会详细讨论,因为调试器在不同浏览器上有所不同,但请查看浏览器的开发人员工具或在 Web 上搜索来获取更多信息。

设置断点的另一种方法,是在程序中包含一个debugger语句(仅由该关键字组成)。 如果你的浏览器的开发人员工具是激活的,则只要程序达到这个语句,程序就会暂停。