哪里出了问题?JavaScript 故障排除
在上一篇文章中构建“猜数字”游戏时,您可能会发现它无法正常工作。不用担心——本文旨在通过提供一些查找和修复 JavaScript 程序错误的方法,让您免于为这些问题而抓狂。
错误类型
一般来说,当您在代码中做错事时,会遇到两种主要类型的错误:
- 语法错误:这些是代码中的拼写错误,实际上会导致程序根本无法运行,或者在运行过程中停止工作——通常还会提供一些错误消息。只要您熟悉正确的工具并知道错误消息的含义,这些错误通常不难修复!
- 逻辑错误:这些是语法实际上正确但代码并非您预期的情况,这意味着程序成功运行但给出不正确的结果。这些错误通常比语法错误更难修复,因为通常没有错误消息可以引导您找到错误的根源。
好的,事情并非那么简单——当您深入研究时,还有其他一些区别。但是,在您职业生涯的这个早期阶段,上述分类就足够了。我们将在接下来探讨这两种类型。
一个错误示例
首先,让我们回到我们的猜数字游戏——只不过这次我们将探索一个引入了一些故意错误的版本。前往 GitHub 并下载一份 number-game-errors.html 的本地副本(在此处查看实时运行)。
- 首先,在您喜欢的文本编辑器和浏览器中打开本地副本。
- 尝试玩游戏——您会注意到,当您按下“提交猜测”按钮时,它不起作用!
注意: 您可能也有一个无法正常工作的游戏示例版本,您可能想修复它!我们仍然希望您使用我们的版本来完成本文,以便您学习我们在此处教授的技术。然后您可以回去尝试修复您的示例。
此时,让我们查看开发者控制台,看看它是否报告了任何语法错误,然后尝试修复它们。您将在下面学习如何操作。
修复语法错误
在本课程的早期,我们让您在 开发者工具 JavaScript 控制台 中输入一些简单的 JavaScript 命令(如果您不记得如何在浏览器中打开它,请点击上一个链接了解如何操作)。更有用的是,每当提供给浏览器 JavaScript 引擎的 JavaScript 中存在语法错误时,控制台都会为您提供错误消息。现在让我们开始查找错误。
-
转到您打开
number-game-errors.html
的选项卡,然后打开您的 JavaScript 控制台。您应该会看到一条类似以下内容的错误消息: -
错误消息的第一行是
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 个字符。
- 第一部分
-
如果我们在代码编辑器中查看第 87 行,我们会找到这一行
jsguessSubmit.addeventListener("click", checkGuess);
-
错误消息说“guessSubmit.addeventListener is not a function”,这意味着我们调用的函数未被 JavaScript 解释器识别。通常,此错误消息实际上意味着我们拼错了什么。如果您不确定某个语法片段的正确拼写,最好在 MDN 上查找该功能。目前最好的方法是使用您喜欢的搜索引擎搜索“mdn 功能名称”。为了在此实例中节省您的时间,这里有一个快捷方式:
addEventListener()
。 -
所以,查看此页面,错误似乎是我们拼错了函数名!请记住 JavaScript 是区分大小写的,因此拼写或大小写上的任何细微差异都会导致错误。将
addeventListener
更改为addEventListener
应该可以解决此问题。现在就执行此操作。
注意: 有关此错误的更多详细信息,请参阅我们的 TypeError: "x" is not a function 参考页面。
语法错误第二轮
-
保存并刷新您的页面,您应该会看到错误消失了。
-
现在,如果您尝试输入一个猜测并按下“提交猜测”按钮,您会看到另一个错误!
-
这次报告的错误是
Uncaught TypeError: can't access property "textContent", lowOrHi is null
根据您使用的浏览器,您可能会在此处看到不同的消息。上面显示的消息是 Firefox 会显示的,但例如 Chrome 会显示这个
Uncaught TypeError: Cannot set properties of null (setting 'textContent')
这是相同的错误,但不同的浏览器以不同的方式描述它。
注意: 此错误在页面加载时没有立即出现,因为此错误发生在函数内部(在
checkGuess() { }
块内部)。正如您将在我们稍后的 函数文章 中更详细地了解到的,函数内部的代码在与函数外部代码不同的作用域中运行。在这种情况下,代码直到第 87 行运行checkGuess()
函数时才运行并抛出错误。 -
错误中给出的行号是 79。查看第 79 行,您会看到以下代码
jslowOrHi.textContent = "Last guess was too high!";
-
这一行试图将
lowOrHi
变量的textContent
属性设置为文本字符串,但它不起作用,因为lowOrHi
没有包含它应该包含的内容。让我们看看这是为什么——尝试在代码中搜索其他lowOrHi
实例。您会找到的最早的实例在第 51 行jsconst lowOrHi = document.querySelector("lowOrHi");
-
此时我们正试图让变量包含对文档 HTML 中元素的引用。让我们看看执行此行后变量包含什么。在第 54 行添加以下代码
jsconsole.log(lowOrHi);
此代码将在第 51 行尝试设置
lowOrHi
的值后将其打印到控制台。有关更多信息,请参阅console.log()
。 -
保存并刷新,您现在应该会在控制台中看到
console.log()
的结果。果然,此时
lowOrHi
的值为null
,这与 Firefox 错误消息lowOrHi is null
相匹配。所以第 51 行肯定有问题。null
值表示“无”或“没有值”。因此,我们将lowOrHi
设置为元素的代码出错了。 -
让我们思考一下问题可能是什么。第 51 行正在使用
document.querySelector()
方法,通过 CSS 选择器获取对元素的引用。进一步向上查看我们的文件,我们可以找到有问题的段落html<p class="lowOrHi"></p>
-
所以我们需要一个类选择器,它以一个点(
.
)开头,但是传入第 51 行querySelector()
方法的选择器没有点。这可能就是问题所在!尝试将第 51 行的lowOrHi
更改为.lowOrHi
。 -
尝试再次保存并刷新,您的
console.log()
语句应该会返回我们想要的<p>
元素。哦!又一个错误修复了!您现在可以删除您的console.log()
行,或者保留它以供以后参考——由您选择。
注意: 有关此错误的更多详细信息,请参阅我们的 TypeError: "x" is (not) "y" 参考页面。
语法错误第三轮
- 现在,如果您再次尝试玩游戏,您应该会更成功——游戏应该可以完全正常进行,直到您结束游戏,无论是通过猜对数字,还是用完猜测次数。
- 此时,游戏再次失败,并吐出了我们一开始就遇到的相同错误——“TypeError: resetButton.addeventListener is not a function”!然而,这次它被列为来自第 95 行。
- 查看第 95 行,很容易看出我们在这里犯了同样的错误。我们仍然只需要将
addeventListener
更改为addEventListener
。现在就执行此操作。
一个逻辑错误
此时,游戏应该会顺利进行,但是玩了几次后,您无疑会注意到游戏总是选择 1 作为您必须猜的“随机”数字。这绝对不是我们希望游戏进行的模式!
游戏逻辑中肯定存在问题——游戏没有返回错误;它只是没有正常运行。
-
搜索
randomNumber
变量,以及首次设置随机数的行。在游戏开始时存储我们想要猜测的随机数的实例应该在第 47 行左右jslet randomNumber = Math.floor(Math.random()) + 1;
-
每次后续游戏之前生成随机数的实例在第 114 行左右
jsrandomNumber = Math.floor(Math.random()) + 1;
-
为了检查这些行是否确实是问题所在,让我们再次求助于我们的朋友
console.log()
——在上面两行之后直接插入以下行jsconsole.log(randomNumber);
-
保存并刷新,然后玩几局游戏——您会看到在每次记录到控制台时,
randomNumber
都等于 1。
理清逻辑
要解决此问题,让我们考虑一下这一行是如何工作的。首先,我们调用 Math.random()
,它生成一个介于 0 和 1 之间的随机小数,例如 0.5675493843。
Math.random();
接下来,我们通过 Math.floor()
传递调用 Math.random()
的结果,它将传递给它的数字向下舍入到最接近的整数。然后我们给那个结果加 1
Math.floor(Math.random()) + 1;
将 0 到 1 之间的随机小数向下舍入将始终返回 0,因此将其加 1 将始终返回 1。我们需要在将其向下舍入之前将随机数乘以 100。以下将给我们一个 0 到 99 之间的随机数
Math.floor(Math.random() * 100);
因此,我们希望加 1,以给我们一个 1 到 100 之间的随机数
Math.floor(Math.random() * 100) + 1;
尝试像这样更新这两行,然后保存并刷新——游戏现在应该按我们预期的那样运行了!
其他常见错误
在您的代码中还会遇到其他常见错误。本节重点介绍了其中大部分。
第一次猜错后游戏结束
这可能是将赋值运算符和严格相等运算符混淆的另一个症状。例如,如果我们要更改 checkGuess()
中的这一行
} else if (guessCount === 10) {
改为
} else if (guessCount = 10) {
测试将始终返回 true
,导致程序在第一次猜错后执行 setGameOver()
。要小心!
SyntaxError: missing ) after argument list
这个非常简单——它通常意味着您在函数/方法调用的末尾遗漏了右括号。
注意: 有关此错误的更多详细信息,请参阅我们的 SyntaxError: missing ) after argument list 参考页面。
SyntaxError: missing : after property id
此错误通常与格式不正确的 JavaScript 对象有关,但在这种情况下,我们通过更改以下内容来获得它
function checkGuess() {
改为
function checkGuess( {
这导致浏览器认为我们正试图将函数的内容作为参数传递给函数。小心那些括号!
SyntaxError: missing } after function body
这很容易——它通常意味着您在函数或条件结构中遗漏了其中一个大括号。我们通过删除 checkGuess()
函数底部附近的一个右大括号来获得此错误。
SyntaxError: expected expression, got 'string' 或 SyntaxError: string literal contains an unescaped line break
这些错误通常意味着您遗漏了字符串值的起始或结束引号。在上面的第一个错误中,string 将被替换为浏览器找到的意外字符(而不是字符串开头的引号)。第二个错误意味着字符串没有以引号结束。
对于所有这些错误,请思考我们如何处理我们在演练中看到的示例。当出现错误时,查看给定的行号,转到该行,看看您是否能发现问题所在。请记住,错误不一定在该行,并且错误可能不是由我们上面提到的完全相同的问题引起的!
注意: 有关这些错误的更多详细信息,请参阅我们的 SyntaxError: Unexpected token 和 SyntaxError: string literal contains an unescaped line break 参考页面。
总结
所以我们已经掌握了,这是找出简单 JavaScript 程序中错误的基础知识。在您的代码中找出错误并非总是那么简单,但至少这可以为您节省几个小时的睡眠,并让您在事情没有顺利进行时更快地取得进展,尤其是在学习旅程的早期阶段。
另见
- 还有许多其他类型的错误未在此处列出;我们正在编写一个详细解释其含义的参考资料——请参阅 JavaScript 错误参考。
- 如果您在阅读本文后遇到任何不确定如何修复的代码错误,您可以获得帮助!在 交流渠道 上寻求帮助。告诉我们您的错误是什么,我们将尽力帮助您。提供您的代码列表也会很有用。