出了什么问题?JavaScript 故障排除

在上一篇文章中,你构建了“猜数字”游戏,你可能发现它无法正常工作。不用担心——本文旨在通过提供一些关于如何在 JavaScript 程序中查找和修复错误的技巧,帮助你避免因这些问题而抓狂。

先决条件 对 HTML 和 CSS 的基本了解,了解 JavaScript 是什么。
目标 获得开始修复自己代码中问题的能力和信心。

错误类型

一般来说,当你犯代码错误时,你会遇到两种主要类型的错误

  • 语法错误:这些是代码中的拼写错误,实际上会导致程序根本无法运行,或在运行过程中停止工作——你通常也会收到一些错误消息。只要你熟悉正确的工具并了解错误消息的含义,这些错误通常很容易修复!
  • 逻辑错误:这些错误是指语法实际上是正确的,但代码并非你想要的那样,这意味着程序成功运行,但结果不正确。这些错误通常比语法错误更难修复,因为通常没有错误消息可以引导你找到错误的来源。

好的,所以情况并非完全那么简单——当你深入研究时,还有一些其他差异。但在你职业生涯的早期阶段,上述分类就足够了。我们将在后续内容中探讨这两种类型的错误。

一个错误示例

首先,让我们回到我们的猜数字游戏——但这次我们将探索一个引入了某些故意错误的版本。访问 GitHub 并为自己创建一个本地副本 number-game-errors.html在线查看)。

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

注意:你可能拥有自己版本的无法正常工作的游戏示例,你可能希望修复它!我们仍然希望你使用我们的版本完成本文,以便学习我们在这里教授的技术。然后你可以返回并尝试修复你的示例。

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

修复语法错误

在课程的早期,我们让你在开发者工具 JavaScript 控制台中输入一些简单的 JavaScript 命令(如果你不记得如何在浏览器中打开它,请按照前面的链接了解如何操作)。更有用的是,只要在发送到浏览器 JavaScript 引擎的 JavaScript 中存在语法错误,控制台就会向你显示错误消息。现在让我们开始搜索吧。

  1. 转到你在其中打开number-game-errors.html的选项卡,并打开你的 JavaScript 控制台。你应该会看到一条类似于以下内容的错误消息:"Number guessing game" demo page in Firefox. One error is visible in the JavaScript console: "X TypeError: guessSubmit.addeventListener is not a function [Learn More] (number-game-errors.html:86:3)".
  2. 错误消息的第一行是
    Uncaught TypeError: guessSubmit.addeventListener is not a function
    number-game-errors.html:86:15
    
    • 第一部分,Uncaught TypeError: guessSubmit.addeventListener is not a function,告诉我们出了什么问题。
    • 第二部分,number-game-errors.html:86:15,告诉我们代码中错误的来源:文件“number-game-errors.html”的第 86 行,第 15 个字符。
  3. 如果我们在代码编辑器中查看第 86 行,我们会找到以下代码行

    警告:错误消息可能不在第 86 行。

    如果你使用任何带有扩展程序的代码编辑器,该扩展程序会在你的本地机器上启动一个实时服务器,这将导致注入额外的代码。因此,开发者工具会将错误列为发生在非 86 行的位置。

    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. 现在,如果你尝试输入猜测并按下“提交猜测”按钮,你将看到另一个错误!Screenshot of the same "Number guessing game" demo. This time, a different error is visible in the console, reading "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() { }块内)。正如你将在我们后面的函数文章中更详细地了解的那样,函数内部的代码与函数外部的代码位于不同的作用域中。在这种情况下,代码没有运行,并且错误直到第 86 行运行checkGuess()函数时才被抛出。

  4. 错误中给出的行号是 80。查看第 80 行,你会看到以下代码
    js
    lowOrHi.textContent = "Last guess was too high!";
    
  5. 此行试图将lowOrHi变量的textContent属性设置为文本字符串,但它不起作用,因为lowOrHi不包含它应该包含的内容。让我们看看为什么会出现这种情况——尝试在代码中搜索其他lowOrHi实例。你将找到的最早的实例在第 49 行
    js
    const lowOrHi = document.querySelector("lowOrHi");
    
  6. 此时,我们试图使变量包含对文档 HTML 中元素的引用。让我们看看在执行此行代码后变量包含什么。在第 50 行添加以下代码
    js
    console.log(lowOrHi);
    
    此代码将在我们尝试在第 49 行设置lowOrHi后将其值打印到控制台。有关更多信息,请参阅console.log()
  7. 保存并刷新,你应该现在可以在控制台中看到console.log()结果。Screenshot of the same demo. One log statement is visible in the console, reading simply "null". 的确,此时lowOrHi的值为null,这与 Firefox 错误消息lowOrHi is null一致。因此第 49 行肯定存在问题。null值表示“无”或“无值”。因此,我们设置lowOrHi为元素的代码出错了。
  8. 让我们考虑一下可能是什么问题。第 49 行使用document.querySelector()方法通过使用 CSS 选择器来获取对元素的引用。进一步查看我们的文件,我们可以找到相关段落
    html
    <p class="lowOrHi"></p>
    
  9. 因此,我们这里需要一个类选择器,它以点 (.) 开头,但传递给第 49 行中querySelector()方法的选择器没有点。这可能是问题所在!尝试在第 49 行将lowOrHi更改为.lowOrHi
  10. 尝试再次保存并刷新,你的console.log()语句应该会返回我们想要的<p>元素。呼!另一个错误已修复!你现在可以删除你的console.log()行,或者保留它以供以后参考——由你决定。

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

语法错误第三轮

  1. 现在,如果你再次尝试玩游戏,你应该会取得更多成功——游戏应该可以正常进行,直到你结束游戏,无论是猜对了数字还是用完了猜测次数。
  2. 在这一点上,游戏再次失败,并且输出了与我们一开始相同的错误——“TypeError: resetButton.addeventListener is not a function”!但是,这次它被列为来自第 94 行。
  3. 查看第 94 行,很容易看出我们在这里犯了同样的错误。我们再次只需要将addeventListener更改为addEventListener。立即执行此操作。

逻辑错误

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

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

  1. 搜索randomNumber变量以及首次设置随机数的行。在游戏开始时存储我们要猜的随机数的实例应该在第 45 行左右
    js
    let randomNumber = Math.floor(Math.random()) + 1;
    
  2. 而在每次后续游戏中生成随机数的实例应该在第 113 行左右
    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.random()的结果传递给Math.floor(),它将传递给它的数字向下舍入到最接近的整数。然后我们将 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
if (userGuess === randomNumber) {

js
if (userGuess = randomNumber) {

则测试将始终返回 true,导致程序报告游戏已获胜。小心!

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 错误参考
  • 如果你在代码中遇到任何不确定如何修复的错误,请在阅读本文后寻求帮助!在 沟通渠道 上寻求帮助。告诉我们你的错误是什么,我们会尽力帮助你。列出你的代码也很有用。