出了什么问题?JavaScript 故障排除
在上一篇文章中,你构建了“猜数字”游戏,你可能发现它无法正常工作。不用担心——本文旨在通过提供一些关于如何在 JavaScript 程序中查找和修复错误的技巧,帮助你避免因这些问题而抓狂。
先决条件 | 对 HTML 和 CSS 的基本了解,了解 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:86:15
- 第一部分,
Uncaught TypeError: guessSubmit.addeventListener is not a function
,告诉我们出了什么问题。 - 第二部分,
number-game-errors.html:86:15
,告诉我们代码中错误的来源:文件“number-game-errors.html”的第 86 行,第 15 个字符。
- 第一部分,
- 如果我们在代码编辑器中查看第 86 行,我们会找到以下代码行
警告:错误消息可能不在第 86 行。
如果你使用任何带有扩展程序的代码编辑器,该扩展程序会在你的本地机器上启动一个实时服务器,这将导致注入额外的代码。因此,开发者工具会将错误列为发生在非 86 行的位置。
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() { }
块内)。正如你将在我们后面的函数文章中更详细地了解的那样,函数内部的代码与函数外部的代码位于不同的作用域中。在这种情况下,代码没有运行,并且错误直到第 86 行运行checkGuess()
函数时才被抛出。 - 错误中给出的行号是 80。查看第 80 行,你会看到以下代码js
lowOrHi.textContent = "Last guess was too high!";
- 此行试图将
lowOrHi
变量的textContent
属性设置为文本字符串,但它不起作用,因为lowOrHi
不包含它应该包含的内容。让我们看看为什么会出现这种情况——尝试在代码中搜索其他lowOrHi
实例。你将找到的最早的实例在第 49 行jsconst lowOrHi = document.querySelector("lowOrHi");
- 此时,我们试图使变量包含对文档 HTML 中元素的引用。让我们看看在执行此行代码后变量包含什么。在第 50 行添加以下代码此代码将在我们尝试在第 49 行设置js
console.log(lowOrHi);
lowOrHi
后将其值打印到控制台。有关更多信息,请参阅console.log()
。 - 保存并刷新,你应该现在可以在控制台中看到
console.log()
结果。 的确,此时lowOrHi
的值为null
,这与 Firefox 错误消息lowOrHi is null
一致。因此第 49 行肯定存在问题。null
值表示“无”或“无值”。因此,我们设置lowOrHi
为元素的代码出错了。 - 让我们考虑一下可能是什么问题。第 49 行使用
document.querySelector()
方法通过使用 CSS 选择器来获取对元素的引用。进一步查看我们的文件,我们可以找到相关段落html<p class="lowOrHi"></p>
- 因此,我们这里需要一个类选择器,它以点 (
.
) 开头,但传递给第 49 行中querySelector()
方法的选择器没有点。这可能是问题所在!尝试在第 49 行将lowOrHi
更改为.lowOrHi
。 - 尝试再次保存并刷新,你的
console.log()
语句应该会返回我们想要的<p>
元素。呼!另一个错误已修复!你现在可以删除你的console.log()
行,或者保留它以供以后参考——由你决定。
注意:有关此错误的更多详细信息,请参阅我们的TypeError: "x" is (not) "y" 参考页面。
语法错误第三轮
- 现在,如果你再次尝试玩游戏,你应该会取得更多成功——游戏应该可以正常进行,直到你结束游戏,无论是猜对了数字还是用完了猜测次数。
- 在这一点上,游戏再次失败,并且输出了与我们一开始相同的错误——“TypeError: resetButton.addeventListener is not a function”!但是,这次它被列为来自第 94 行。
- 查看第 94 行,很容易看出我们在这里犯了同样的错误。我们再次只需要将
addeventListener
更改为addEventListener
。立即执行此操作。
逻辑错误
此时,游戏应该可以正常进行,但是玩过几次后,你无疑会注意到游戏总是选择 1 作为你必须猜的“随机”数字。这绝对不是我们希望游戏进行的方式!
游戏逻辑中肯定存在问题——游戏没有返回错误;它只是没有正确地进行。
- 搜索
randomNumber
变量以及首次设置随机数的行。在游戏开始时存储我们要猜的随机数的实例应该在第 45 行左右jslet randomNumber = Math.floor(Math.random()) + 1;
- 而在每次后续游戏中生成随机数的实例应该在第 113 行左右js
randomNumber = Math.floor(Math.random()) + 1;
- 为了检查这些行是否确实是问题所在,让我们再次求助于我们的朋友
console.log()
——在上述两行代码的正下方插入以下代码行jsconsole.log(randomNumber);
- 保存并刷新,然后玩几局游戏——你将看到在控制台中记录的每个位置
randomNumber
都等于 1。
贯彻逻辑
为了解决此问题,让我们考虑一下此行代码是如何工作的。首先,我们调用Math.random()
,它生成一个介于 0 和 1 之间的随机十进制数,例如 0.5675493843。
Math.random();
接下来,我们将调用Math.random()
的结果传递给Math.floor()
,它将传递给它的数字向下舍入到最接近的整数。然后我们将 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()
函数内部更改以下代码行:
if (userGuess === randomNumber) {
为
if (userGuess = randomNumber) {
则测试将始终返回 true
,导致程序报告游戏已获胜。小心!
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 错误参考。
- 如果你在代码中遇到任何不确定如何修复的错误,请在阅读本文后寻求帮助!在 沟通渠道 上寻求帮助。告诉我们你的错误是什么,我们会尽力帮助你。列出你的代码也很有用。