什么地方出了错?JavaScript 疑难解答
当你在上一篇文章中构建 "猜数字" 游戏时,你可能会发现它不起作用。别担心 - 本文旨在为你提供一些有关如何查找和修复 JavaScript 程序中的错误的提示,从而使你免于为此类问题而烦恼。
¥When you built up the "Guess the number" game in the previous article, you may have found that it didn't work. Never fear — this article aims to save you from tearing your hair out over such problems by providing you with some tips on how to find and fix errors in JavaScript programs.
先决条件: | 对 HTML 和 CSS 有基本的了解,对 JavaScript 有了解。 |
---|---|
目标: | 获得开始修复自己代码中的问题的能力和信心。 |
错误类型
¥Types of error
一般来说,当你在代码中出现错误时,你会遇到两种主要类型的错误:
¥Generally speaking, when you do something wrong in code, there are two main types of error that you'll come across:
- 语法错误:这些是代码中的拼写错误,实际上会导致程序根本无法运行,或者中途停止工作 - 通常也会向你提供一些错误消息。只要你熟悉正确的工具并知道错误消息的含义,这些通常就可以修复!
- 逻辑错误:这些错误的语法实际上是正确的,但代码不是你想要的,这意味着程序成功运行但给出了错误的结果。这些错误通常比语法错误更难修复,因为通常没有错误消息来引导你找到错误的根源。
好吧,事情没那么简单 - 当你深入研究时,还有一些其他的差异化因素。但上述分类适用于你职业生涯的早期阶段。我们将继续研究这两种类型。
¥Okay, so it's not quite that simple — there are some other differentiators as you drill down deeper. But the above classifications will do at this early stage in your career. We'll look at both of these types going forward.
一个错误的例子
¥An erroneous example
首先,让我们回到我们的猜数字游戏 - 只不过这次我们将探索一个故意引入一些错误的版本。转到 GitHub 并为自己制作 number-game-errors.html 的本地副本(参见 在这里实时运行)。
¥To get started, let's return to our number guessing game — except this time we'll be exploring a version that has some deliberate errors introduced. Go to GitHub and make yourself a local copy of number-game-errors.html (see it running live here).
- 首先,请在你喜欢的文本编辑器和浏览器中打开本地副本。
- 尝试玩游戏 - 你会发现当你按 "提交猜测" 按钮时,它不起作用!
注意:你可能有自己的游戏示例版本无法运行,你可能想要修复它!我们仍然希望你使用我们的版本来完成本文,以便你可以学习我们在这里教授的技术。然后你可以返回并尝试修复你的示例。
¥Note: You might well have your own version of the game example that doesn't work, which you might want to fix! We'd still like you to work through the article with our version, so that you can learn the techniques we are teaching here. Then you can go back and try to fix your example.
此时,让我们查阅开发者控制台,看看它是否报告任何语法错误,然后尝试修复它们。你将在下面了解如何操作。
¥At this point, let's consult the developer console to see if it reports any syntax errors, then try to fix them. You'll learn how below.
修复语法错误
¥Fixing syntax errors
在课程的前面,我们让你在 开发者工具 JavaScript 控制台 中输入一些简单的 JavaScript 命令(如果你不记得如何在浏览器中打开它,请按照前面的链接了解如何操作)。更有用的是,只要输入浏览器 JavaScript 引擎的 JavaScript 内存在语法错误,控制台就会向你提供错误消息。现在我们去打猎吧。
¥Earlier on in the course we got you to type some simple JavaScript commands into the developer tools JavaScript console (if you can't remember how to open this in your browser, follow the previous link to find out how). What's even more useful is that the console gives you error messages whenever a syntax error exists inside the JavaScript being fed into the browser's JavaScript engine. Now let's go hunting.
- 转到打开
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 行。
¥Warning: Error message may not be on line 86.
如果你使用任何具有在本地计算机上启动实时服务器的扩展的代码编辑器,这将导致注入额外的代码。因此,开发者工具会将错误列为发生在非 86 行上。
¥If you are using any code editor with an extension that launches a live server on your local machine, this will cause extra code to be injected. Because of this, the developer tools will list the error as occurring on a line that is not 86.
jsguessSubmit.addeventListener("click", checkGuess);
- 错误消息显示 "guessSubmit.addeventListener 不是一个函数",这意味着 JavaScript 解释器无法识别我们正在调用的函数。通常,此错误消息实际上意味着我们拼写错误。如果你不确定某个语法的拼写是否正确,最好在 MDN 上查找该功能。目前最好的方法是使用你最喜欢的搜索引擎搜索 "mdn 功能名称"。在这种情况下,有一个可以节省你时间的快捷方式:
addEventListener()
。 - 因此,查看此页面,错误似乎是我们将函数名称拼写错误!请记住,JavaScript 区分大小写,因此拼写或大小写上的任何细微差别都会导致错误。将
addeventListener
更改为addEventListener
应该可以解决此问题。现在就这样做。
注意:有关此错误的更多详细信息,请参阅我们的 类型错误:"x" 不是函数 参考页。
¥Note: See our TypeError: "x" is not a function reference page for more details about this error.
第二轮语法错误
¥Syntax errors round two
- 保存页面并刷新,你应该会看到错误已经消失。
- 现在,如果你尝试输入猜测并按“提交猜测”按钮,你将看到另一个错误!
- 这次报的错误是:
Uncaught TypeError: can't access property "textContent", lowOrHi is null
根据你使用的浏览器,你可能会在此处看到不同的消息。上面的消息是 Firefox 将向你显示的内容,但 Chrome 会向你显示以下内容:Uncaught TypeError: Cannot set properties of null (setting 'textContent')
这是相同的错误,但不同的浏览器以不同的方式描述它。注意:页面加载后并没有立即出现此错误,因为此错误发生在函数内部(
checkGuess() { }
块内)。正如你将在后面的 功能文章 中更详细地了解到的那样,函数内部的代码与函数外部的代码运行在不同的作用域中。在这种情况下,直到第 86 行运行checkGuess()
函数后,代码才运行,并且不会引发错误。¥Note: This error didn't come up as soon as the page was loaded because this error occurred inside a function (inside the
checkGuess() { }
block). As you'll learn in more detail in our later functions article, code inside functions runs in a separate scope than code outside functions. In this case, the code was not run and the error was not thrown until thecheckGuess()
function was run by line 86. - 错误中给出的行号是 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
值表示 "nothing" 或 "没有价值"。所以我们将 lowOrHi
设置为元素的代码出错了。
¥Sure enough, lowOrHi
's value is null
at this point, and this matches up with the Firefox error message lowOrHi is null
. So there is definitely a problem with line 49. The null
value means "nothing", or "no value". So our code to set lowOrHi
to an element is going wrong.
- 让我们想想问题可能出在哪里。第 49 行使用
document.querySelector()
方法通过 CSS 选择器选择元素来获取对元素的引用。进一步查看我们的文件,我们可以找到有问题的段落:html<p class="lowOrHi"></p>
- 所以我们这里需要一个类选择器,它以点(
.
)开头,但是第 49 行传递到querySelector()
方法的选择器没有点。这可能是问题所在!尝试将第 49 行中的lowOrHi
更改为.lowOrHi
。 - 再次尝试保存并刷新,你的
console.log()
语句应该返回我们想要的<p>
元素。唷!另一个错误已修复!你现在可以删除console.log()
行,也可以保留它以供日后参考 - 你可以选择。
注意:有关此错误的更多详细信息,请参阅我们的 类型错误:"x" 是(不是)"y" 参考页。
¥Note: See our TypeError: "x" is (not) "y" reference page for more details about this error.
第三轮语法错误
逻辑错误
¥A logic error
此时,游戏应该可以正常运行,但是玩过几次后,你无疑会注意到游戏总是选择 1 作为你必须猜测的 "random" 数字。绝对不是我们想要的游戏玩法!
¥At this point, the game should play through fine, however after playing through a few times you'll undoubtedly notice that the game always chooses 1 as the "random" number you've got to guess. Definitely not quite how we want the game to play out!
游戏逻辑肯定有问题 - 游戏没有返回错误;它只是播放不正确。
¥There's definitely a problem in the game logic somewhere — the game is not returning an error; it just isn't playing right.
- 搜索
randomNumber
变量以及首次设置随机数的行。存储我们想要在游戏开始时猜测的随机数的实例应该在第 45 行左右:jslet randomNumber = Math.floor(Math.random()) + 1;
- 在接下来的每场比赛之前生成随机数的代码位于第 113 行左右:
js
randomNumber = Math.floor(Math.random()) + 1;
- 为了检查这些行是否确实是问题所在,让我们再次求助于我们的朋友
console.log()
— 在上述两行的正下方插入以下行:jsconsole.log(randomNumber);
- 保存并刷新,然后玩几场游戏 - 你将看到
randomNumber
在记录到控制台的每个点上都等于 1。
通过逻辑进行工作
¥Working through the logic
为了解决这个问题,让我们考虑一下这条线是如何工作的。首先,我们调用 Math.random()
,它生成一个 0 到 1 之间的随机十进制数,例如 0.5675493843。
¥To fix this, let's consider how this line is working. First, we invoke Math.random()
, which generates a random decimal number between 0 and 1, e.g. 0.5675493843.
Math.random();
接下来,我们将调用 Math.random()
的结果传递到 Math.floor()
,这会将传递给它的数字向下舍入到最接近的整数。然后我们将结果加 1:
¥Next, we pass the result of invoking Math.random()
through Math.floor()
, which rounds the number passed to it down to the nearest whole number. We then add 1 to that result:
Math.floor(Math.random()) + 1;
将 0 到 1 之间的随机小数向下舍入将始终返回 0,因此加 1 将始终返回 1。在向下舍入之前,我们需要将随机数乘以 100。下面的代码会给我们一个 0 到 99 之间的随机数:
¥Rounding a random decimal number between 0 and 1 down will always return 0, so adding 1 to it will always return 1. We need to multiply the random number by 100 before we round it down. The following would give us a random number between 0 and 99:
Math.floor(Math.random() * 100);
因此我们想加 1,得到一个 1 到 100 之间的随机数:
¥Hence us wanting to add 1, to give us a random number between 1 and 100:
Math.floor(Math.random() * 100) + 1;
尝试像这样更新两行,然后保存并刷新 - 游戏现在应该像我们想要的那样玩!
¥Try updating both lines like this, then save and refresh — the game should now play like we are intending it to!
其他常见错误
无论你输入的猜测如何,程序总是说你赢了
¥The program always says you've won, regardless of the guess you enter
这可能是混淆赋值运算符和严格相等运算符的另一个症状。例如,如果我们要更改 checkGuess()
内的这一行:
¥This could be another symptom of mixing up the assignment and strict equality operators. For example, if we were to change this line inside checkGuess()
:
if (userGuess === randomNumber) {
to
if (userGuess = randomNumber) {
测试将始终返回 true
,导致程序报告游戏已获胜。当心!
¥the test would always return true
, causing the program to report that the game has been won. Be careful!
语法错误:参数列表后缺少 )
¥SyntaxError: missing ) after argument list
这个非常简单 - 它通常意味着你遗漏了函数/方法调用末尾的右括号。
¥This one is pretty simple — it generally means that you've missed the closing parenthesis at the end of a function/method call.
注意:有关此错误的更多详细信息,请参阅我们的 语法错误:参数列表后缺少 ) 参考页。
¥Note: See our SyntaxError: missing ) after argument list reference page for more details about this error.
语法错误:缺少:属性 id 之后
¥SyntaxError: missing : after property id
此错误通常与格式不正确的 JavaScript 对象有关,但在这种情况下,我们设法通过更改来获取它
¥This error usually relates to an incorrectly formed JavaScript object, but in this case we managed to get it by changing
function checkGuess() {
to
function checkGuess( {
这导致浏览器认为我们正在尝试将函数的内容作为参数传递到函数中。小心这些括号!
¥This has caused the browser to think that we are trying to pass the contents of the function into the function as an argument. Be careful with those parentheses!
语法错误:函数体后面缺少 }
¥SyntaxError: missing } after function body
这很简单 - 这通常意味着你遗漏了函数或条件结构中的一个大括号。我们通过删除 checkGuess()
函数底部附近的右大括号之一来得到此错误。
¥This is easy — it generally means that you've missed one of your curly braces from a function or conditional structure. We got this error by deleting one of the closing curly braces near the bottom of the checkGuess()
function.
语法错误:预期的表达式,得到 'string' 或 SyntaxError:字符串文字包含未转义的换行符
¥SyntaxError: expected expression, got 'string' or SyntaxError: string literal contains an unescaped line break
这些错误通常意味着你遗漏了字符串值的左引号或右引号。在上面的第一个错误中,字符串将被浏览器发现的意外字符替换,而不是字符串开头的引号。第二个错误意味着字符串没有以引号结束。
¥These errors generally mean that you've left off a string value's opening or closing quote mark. In the first error above, string would be replaced with the unexpected character(s) that the browser found instead of a quote mark at the start of a string. The second error means that the string has not been ended with a quote mark.
对于所有这些错误,请考虑一下我们如何解决演练中看到的示例。当出现错误时,查看给定的行号,转到该行,看看是否可以发现问题所在。请记住,错误不一定会出现在该行上,而且错误可能不是由我们上面引用的完全相同的问题引起的!
¥For all of these errors, think about how we tackled the examples we looked at in the walkthrough. When an error arises, look at the line number you are given, go to that line and see if you can spot what's wrong. Bear in mind that the error is not necessarily going to be on that line, and also that the error might not be caused by the exact same problem we cited above!
注意:有关这些错误的更多详细信息,请参阅我们的 语法错误:意外的标记 和 语法错误:字符串文字包含未转义的换行符 参考页。
¥Note: See our SyntaxError: Unexpected token and SyntaxError: string literal contains an unescaped line break reference pages for more details about these errors.
概括
¥Summary
现在我们已经掌握了找出简单 JavaScript 程序中错误的基础知识。找出代码中的问题并不总是那么简单,但至少这会节省你几个小时的睡眠,并让你在事情出现问题时进步得更快一些,尤其是在早期 你的学习旅程的各个阶段。
¥So there we have it, the basics of figuring out errors in simple JavaScript programs. It won't always be that simple to work out what's wrong in your code, but at least this will save you a few hours of sleep and allow you to progress a bit faster when things don't turn out right, especially in the earlier stages of your learning journey.
也可以看看
¥See also
- 还有许多其他类型的错误,此处未列出;我们正在编写一份参考资料,详细解释它们的含义 - 请参阅 JavaScript 错误参考。
- 如果你在代码中遇到任何错误,并且在阅读本文后不确定如何修复,你可以寻求帮助!寻求有关 沟通渠道 的帮助。告诉我们你的错误是什么,我们将尽力帮助你。你的代码列表也会很有用。