哪里出了问题?JavaScript 故障排除

在上一篇文章中构建“猜数字”游戏时,您可能会发现它无法正常工作。不用担心——本文旨在通过提供一些查找和修复 JavaScript 程序错误的方法,让您免于为这些问题而抓狂。

预备知识 了解 HTMLCSS 基础知识,以及编写 JavaScript 的基本经验。
学习成果
  • 了解 JavaScript 中可能发生的错误类型。
  • 使用 console.log() 调试错误。
  • 使用浏览器 DevTools JavaScript 控制台的基本经验。
  • 熟悉 JavaScript 错误消息及其含义。

错误类型

一般来说,当您在代码中做错事时,会遇到两种主要类型的错误:

  • 语法错误:这些是代码中的拼写错误,实际上会导致程序根本无法运行,或者在运行过程中停止工作——通常还会提供一些错误消息。只要您熟悉正确的工具并知道错误消息的含义,这些错误通常不难修复!
  • 逻辑错误:这些是语法实际上正确但代码并非您预期的情况,这意味着程序成功运行但给出不正确的结果。这些错误通常比语法错误更难修复,因为通常没有错误消息可以引导您找到错误的根源。

好的,事情并非那么简单——当您深入研究时,还有其他一些区别。但是,在您职业生涯的这个早期阶段,上述分类就足够了。我们将在接下来探讨这两种类型。

一个错误示例

首先,让我们回到我们的猜数字游戏——只不过这次我们将探索一个引入了一些故意错误的版本。前往 GitHub 并下载一份 number-game-errors.html 的本地副本(在此处查看实时运行)。

  1. 首先,在您喜欢的文本编辑器和浏览器中打开本地副本。
  2. 尝试玩游戏——您会注意到,当您按下“提交猜测”按钮时,它不起作用!

注意: 您可能也有一个无法正常工作的游戏示例版本,您可能想修复它!我们仍然希望您使用我们的版本来完成本文,以便您学习我们在此处教授的技术。然后您可以回去尝试修复您的示例。

此时,让我们查看开发者控制台,看看它是否报告了任何语法错误,然后尝试修复它们。您将在下面学习如何操作。

修复语法错误

在本课程的早期,我们让您在 开发者工具 JavaScript 控制台 中输入一些简单的 JavaScript 命令(如果您不记得如何在浏览器中打开它,请点击上一个链接了解如何操作)。更有用的是,每当提供给浏览器 JavaScript 引擎的 JavaScript 中存在语法错误时,控制台都会为您提供错误消息。现在让我们开始查找错误。

  1. 转到您打开 number-game-errors.html 的选项卡,然后打开您的 JavaScript 控制台。您应该会看到一条类似以下内容的错误消息:Firefox 中的“猜数字游戏”演示页面。JavaScript 控制台中可见一个错误:“X TypeError: guessSubmit.addeventListener is not a function [了解更多] (number-game-errors.html:87:19)”。

  2. 错误消息的第一行是

    Uncaught TypeError: guessSubmit.addeventListener is not a function
    number-game-errors.html:87:19
    
    • 第一部分 Uncaught TypeError: guessSubmit.addeventListener is not a function 告诉我们出了什么问题。
    • 第二部分 number-game-errors.html:87:19 告诉我们错误来自代码的哪个位置:文件 "number-game-errors.html" 的第 87 行第 19 个字符。
  3. 如果我们在代码编辑器中查看第 87 行,我们会找到这一行

    js
    guessSubmit.addeventListener("click", checkGuess);
    
  4. 错误消息说“guessSubmit.addeventListener is not a function”,这意味着我们调用的函数未被 JavaScript 解释器识别。通常,此错误消息实际上意味着我们拼错了什么。如果您不确定某个语法片段的正确拼写,最好在 MDN 上查找该功能。目前最好的方法是使用您喜欢的搜索引擎搜索“mdn 功能名称”。为了在此实例中节省您的时间,这里有一个快捷方式:addEventListener()

  5. 所以,查看此页面,错误似乎是我们拼错了函数名!请记住 JavaScript 是区分大小写的,因此拼写或大小写上的任何细微差异都会导致错误。将 addeventListener 更改为 addEventListener 应该可以解决此问题。现在就执行此操作。

注意: 有关此错误的更多详细信息,请参阅我们的 TypeError: "x" is not a function 参考页面。

语法错误第二轮

  1. 保存并刷新您的页面,您应该会看到错误消失了。

  2. 现在,如果您尝试输入一个猜测并按下“提交猜测”按钮,您会看到另一个错误!相同“猜数字游戏”演示的截图。这次,控制台中可见一个不同的错误,显示为“X TypeError: lowOrHi is null”。

  3. 这次报告的错误是

    Uncaught TypeError: can't access property "textContent", lowOrHi is null
    

    根据您使用的浏览器,您可能会在此处看到不同的消息。上面显示的消息是 Firefox 会显示的,但例如 Chrome 会显示这个

    Uncaught TypeError: Cannot set properties of null (setting 'textContent')
    

    这是相同的错误,但不同的浏览器以不同的方式描述它。

    注意: 此错误在页面加载时没有立即出现,因为此错误发生在函数内部(在 checkGuess() { } 块内部)。正如您将在我们稍后的 函数文章 中更详细地了解到的,函数内部的代码在与函数外部代码不同的作用域中运行。在这种情况下,代码直到第 87 行运行 checkGuess() 函数时才运行并抛出错误。

  4. 错误中给出的行号是 79。查看第 79 行,您会看到以下代码

    js
    lowOrHi.textContent = "Last guess was too high!";
    
  5. 这一行试图将 lowOrHi 变量的 textContent 属性设置为文本字符串,但它不起作用,因为 lowOrHi 没有包含它应该包含的内容。让我们看看这是为什么——尝试在代码中搜索其他 lowOrHi 实例。您会找到的最早的实例在第 51 行

    js
    const lowOrHi = document.querySelector("lowOrHi");
    
  6. 此时我们正试图让变量包含对文档 HTML 中元素的引用。让我们看看执行此行后变量包含什么。在第 54 行添加以下代码

    js
    console.log(lowOrHi);
    

    此代码将在第 51 行尝试设置 lowOrHi 的值后将其打印到控制台。有关更多信息,请参阅 console.log()

  7. 保存并刷新,您现在应该会在控制台中看到 console.log() 的结果。相同演示的截图。控制台中可见一条日志语句,仅显示“null”。 果然,此时 lowOrHi 的值为 null,这与 Firefox 错误消息 lowOrHi is null 相匹配。所以第 51 行肯定有问题。 null 值表示“无”或“没有值”。因此,我们将 lowOrHi 设置为元素的代码出错了。

  8. 让我们思考一下问题可能是什么。第 51 行正在使用 document.querySelector() 方法,通过 CSS 选择器获取对元素的引用。进一步向上查看我们的文件,我们可以找到有问题的段落

    html
    <p class="lowOrHi"></p>
    
  9. 所以我们需要一个类选择器,它以一个点(.)开头,但是传入第 51 行 querySelector() 方法的选择器没有点。这可能就是问题所在!尝试将第 51 行的 lowOrHi 更改为 .lowOrHi

  10. 尝试再次保存并刷新,您的 console.log() 语句应该会返回我们想要的 <p> 元素。哦!又一个错误修复了!您现在可以删除您的 console.log() 行,或者保留它以供以后参考——由您选择。

注意: 有关此错误的更多详细信息,请参阅我们的 TypeError: "x" is (not) "y" 参考页面。

语法错误第三轮

  1. 现在,如果您再次尝试玩游戏,您应该会更成功——游戏应该可以完全正常进行,直到您结束游戏,无论是通过猜对数字,还是用完猜测次数。
  2. 此时,游戏再次失败,并吐出了我们一开始就遇到的相同错误——“TypeError: resetButton.addeventListener is not a function”!然而,这次它被列为来自第 95 行。
  3. 查看第 95 行,很容易看出我们在这里犯了同样的错误。我们仍然只需要将 addeventListener 更改为 addEventListener。现在就执行此操作。

一个逻辑错误

此时,游戏应该会顺利进行,但是玩了几次后,您无疑会注意到游戏总是选择 1 作为您必须猜的“随机”数字。这绝对不是我们希望游戏进行的模式!

游戏逻辑中肯定存在问题——游戏没有返回错误;它只是没有正常运行。

  1. 搜索 randomNumber 变量,以及首次设置随机数的行。在游戏开始时存储我们想要猜测的随机数的实例应该在第 47 行左右

    js
    let randomNumber = Math.floor(Math.random()) + 1;
    
  2. 每次后续游戏之前生成随机数的实例在第 114 行左右

    js
    randomNumber = Math.floor(Math.random()) + 1;
    
  3. 为了检查这些行是否确实是问题所在,让我们再次求助于我们的朋友 console.log()——在上面两行之后直接插入以下行

    js
    console.log(randomNumber);
    
  4. 保存并刷新,然后玩几局游戏——您会看到在每次记录到控制台时,randomNumber 都等于 1。

理清逻辑

要解决此问题,让我们考虑一下这一行是如何工作的。首先,我们调用 Math.random(),它生成一个介于 0 和 1 之间的随机小数,例如 0.5675493843。

js
Math.random();

接下来,我们通过 Math.floor() 传递调用 Math.random() 的结果,它将传递给它的数字向下舍入到最接近的整数。然后我们给那个结果加 1

js
Math.floor(Math.random()) + 1;

将 0 到 1 之间的随机小数向下舍入将始终返回 0,因此将其加 1 将始终返回 1。我们需要在将其向下舍入之前将随机数乘以 100。以下将给我们一个 0 到 99 之间的随机数

js
Math.floor(Math.random() * 100);

因此,我们希望加 1,以给我们一个 1 到 100 之间的随机数

js
Math.floor(Math.random() * 100) + 1;

尝试像这样更新这两行,然后保存并刷新——游戏现在应该按我们预期的那样运行了!

其他常见错误

在您的代码中还会遇到其他常见错误。本节重点介绍了其中大部分。

第一次猜错后游戏结束

这可能是将赋值运算符和严格相等运算符混淆的另一个症状。例如,如果我们要更改 checkGuess() 中的这一行

js
} else if (guessCount === 10) {

改为

js
} else if (guessCount = 10) {

测试将始终返回 true,导致程序在第一次猜错后执行 setGameOver()。要小心!

SyntaxError: missing ) after argument list

这个非常简单——它通常意味着您在函数/方法调用的末尾遗漏了右括号。

注意: 有关此错误的更多详细信息,请参阅我们的 SyntaxError: missing ) after argument list 参考页面。

SyntaxError: missing : after property id

此错误通常与格式不正确的 JavaScript 对象有关,但在这种情况下,我们通过更改以下内容来获得它

js
function checkGuess() {

改为

js
function checkGuess( {

这导致浏览器认为我们正试图将函数的内容作为参数传递给函数。小心那些括号!

SyntaxError: missing } after function body

这很容易——它通常意味着您在函数或条件结构中遗漏了其中一个大括号。我们通过删除 checkGuess() 函数底部附近的一个右大括号来获得此错误。

SyntaxError: expected expression, got 'string' 或 SyntaxError: string literal contains an unescaped line break

这些错误通常意味着您遗漏了字符串值的起始或结束引号。在上面的第一个错误中,string 将被替换为浏览器找到的意外字符(而不是字符串开头的引号)。第二个错误意味着字符串没有以引号结束。

对于所有这些错误,请思考我们如何处理我们在演练中看到的示例。当出现错误时,查看给定的行号,转到该行,看看您是否能发现问题所在。请记住,错误不一定在该行,并且错误可能不是由我们上面提到的完全相同的问题引起的!

注意: 有关这些错误的更多详细信息,请参阅我们的 SyntaxError: Unexpected tokenSyntaxError: string literal contains an unescaped line break 参考页面。

总结

所以我们已经掌握了,这是找出简单 JavaScript 程序中错误的基础知识。在您的代码中找出错误并非总是那么简单,但至少这可以为您节省几个小时的睡眠,并让您在事情没有顺利进行时更快地取得进展,尤其是在学习旅程的早期阶段。

另见

  • 还有许多其他类型的错误未在此处列出;我们正在编写一个详细解释其含义的参考资料——请参阅 JavaScript 错误参考
  • 如果您在阅读本文后遇到任何不确定如何修复的代码错误,您可以获得帮助!在 交流渠道 上寻求帮助。告诉我们您的错误是什么,我们将尽力帮助您。提供您的代码列表也会很有用。