JavaScript 初体验

现在您已经了解了一些关于 JavaScript 理论及其用途的信息,我们将通过一个实用的教程,让您了解创建简单的 JavaScript 程序的过程。在这里,您将逐步构建一个简单的“猜数字”游戏。

先决条件 对 HTML 和 CSS 的基本了解,以及对 JavaScript 的了解。
目标 获得编写一些 JavaScript 代码的初步经验,并至少对编写 JavaScript 程序涉及的内容有一个基本了解。

我们希望在这里设定非常明确的期望:我们不希望您在阅读完本文后就能学会 JavaScript,甚至不希望您理解我们要求您编写的全部代码。相反,我们希望让您了解 JavaScript 的特性是如何协同工作的,以及编写 JavaScript 的感觉。在后续的文章中,您将更详细地重新学习此处显示的所有功能,因此,如果您无法立即理解所有内容,请不要担心!

注意:您将在 JavaScript 中看到的许多代码特性与其他编程语言中的特性相同——函数、循环等。代码语法看起来有所不同,但概念在很大程度上仍然相同。

像程序员一样思考

在编程中,最难学习的事情之一不是需要学习的语法,而是如何应用它来解决现实世界中的问题。您需要开始像程序员一样思考——这通常涉及查看程序需要执行的操作的描述,确定实现这些操作所需的代码功能,以及如何使它们协同工作。

这需要努力工作、对编程语法的经验和实践——再加上一点创造力。您编写的代码越多,您就会越擅长它。我们不能保证您能在五分钟内培养出“程序员思维”,但我们将在整个课程中为您提供大量练习像程序员一样思考的机会。

考虑到这一点,让我们看看本文中将要构建的示例,并回顾将其分解为具体任务的总体过程。

示例 — 猜数字游戏

在本文中,我们将向您展示如何构建下面您看到的简单游戏

尝试玩一下——在继续之前,先熟悉一下游戏。

假设您的老板给您提供了以下创建此游戏的简要说明

我希望您创建一个简单的猜数字类型的游戏。它应该在 1 到 100 之间选择一个随机数,然后挑战玩家在 10 轮内猜出这个数字。在每一轮之后,应该告诉玩家他们是否猜对了,如果猜错了,则告知猜测是太低还是太高。它还应该告诉玩家他们之前猜过的数字。一旦玩家猜对或用完轮数,游戏就会结束。当游戏结束时,应该给玩家一个重新开始游戏的选项。

在查看了这个简要说明后,我们首先可以做的是开始将其分解成简单的可操作任务,尽可能地以程序员的心态进行

  1. 生成 1 到 100 之间的随机数。
  2. 记录玩家当前进行的轮数。从 1 开始。
  3. 为玩家提供一种猜测数字的方法。
  4. 提交猜测后,首先将其记录在某个地方,以便用户可以查看其之前的猜测。
  5. 接下来,检查它是否为正确的数字。
  6. 如果正确
    1. 显示祝贺信息。
    2. 阻止玩家输入更多猜测(这会破坏游戏)。
    3. 显示允许玩家重新开始游戏的控件。
  7. 如果错误且玩家还有剩余轮数
    1. 告诉玩家他们错了,以及他们的猜测是太高还是太低。
    2. 允许他们输入另一个猜测。
    3. 将轮数增加 1。
  8. 如果错误且玩家没有剩余轮数
    1. 告诉玩家游戏结束。
    2. 阻止玩家输入更多猜测(这会破坏游戏)。
    3. 显示允许玩家重新开始游戏的控件。
  9. 游戏重新开始后,确保游戏逻辑和 UI 完全重置,然后返回步骤 1。

现在让我们继续前进,看看如何将这些步骤转换为代码,构建示例,并在过程中探索 JavaScript 功能。

初始设置

要开始本教程,我们希望您创建 number-guessing-game-start.html 文件的本地副本(请在此处查看)。在您的文本编辑器和 Web 浏览器中打开它。目前,您将看到一个简单的标题、一段说明和一个用于输入猜测的表单,但该表单目前不会执行任何操作。

我们将添加所有代码的位置位于 HTML 底部的 <script> 元素内部

html
<script>
  // Your JavaScript goes here
</script>

添加变量来存储我们的数据

让我们开始吧。首先,在您的 <script> 元素内部添加以下行

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

const guesses = document.querySelector(".guesses");
const lastResult = document.querySelector(".lastResult");
const lowOrHi = document.querySelector(".lowOrHi");

const guessSubmit = document.querySelector(".guessSubmit");
const guessField = document.querySelector(".guessField");

let guessCount = 1;
let resetButton;

此代码段设置了我们需要存储程序将使用的数据的变量和常量。

变量基本上是值的名称(例如数字或文本字符串)。您可以使用关键字 let 后跟变量名称来创建变量。

常量也用于命名值,但与变量不同,一旦设置,您就无法更改其值。在本例中,我们使用常量来存储对用户界面部分的引用。这些元素中的一些文本可能会更改,但每个常量始终引用其初始化时相同的 HTML 元素。您可以使用关键字 const 后跟常量名称来创建常量。

您可以使用等号 (=) 后跟要赋予它的值,将值赋予变量或常量。

在我们的示例中

  • 第一个变量——randomNumber——被赋予 1 到 100 之间的随机数,该随机数是使用数学算法计算的。
  • 前三个常量分别用于存储对 HTML 中结果段落的引用,并用于在代码的后面将值插入段落中(请注意它们位于 <div> 元素内部,该元素本身用于稍后选择所有三个元素以在重新开始游戏时进行重置)。
    html
    <div class="resultParas">
      <p class="guesses"></p>
      <p class="lastResult"></p>
      <p class="lowOrHi"></p>
    </div>
    
  • 接下来的两个常量存储对表单文本输入和提交按钮的引用,并用于稍后控制提交猜测。
    html
    <label for="guessField">Enter a guess: </label>
    <input type="number" id="guessField" class="guessField" />
    <input type="submit" value="Submit guess" class="guessSubmit" />
    
  • 最后两个变量存储猜测次数 1(用于跟踪玩家进行了多少次猜测)和对尚不存在的重置按钮的引用(但稍后将存在)。

注意:您将在课程的后面学习更多关于变量和常量的知识,从文章 存储您需要的信息——变量 开始。

函数

接下来,在您之前的 JavaScript 下方添加以下内容

js
function checkGuess() {
  alert("I am a placeholder");
}

函数是可以重用的代码块,您可以编写一次并反复运行,从而避免每次都重复编写代码。这非常有用。定义函数的方法有很多,但现在我们将专注于一种简单类型。在这里,我们通过使用关键字 function 后跟名称以及括号来定义了一个函数。之后,我们放置两个花括号 ({ })。在花括号内放置我们希望在每次调用函数时运行的所有代码。

当我们想要运行代码时,我们键入函数名称后跟括号。

现在让我们尝试一下。保存您的代码并在浏览器中刷新页面。然后进入 开发者工具 JavaScript 控制台,并输入以下行

js
checkGuess();

按下 Return/Enter 后,您应该会看到一个弹出窗口,显示 我是一个占位符;我们在代码中定义了一个函数,该函数会在每次调用它时创建一个警报。

注意:您将在文章 函数——可重用的代码块 中学习更多关于函数的知识。

运算符

JavaScript 运算符允许我们执行测试、进行数学运算、将字符串连接在一起以及其他此类操作。

如果您尚未执行此操作,请保存您的代码,在浏览器中刷新页面,并打开 开发者工具 JavaScript 控制台。然后,我们可以尝试键入下面显示的示例——按原样键入“示例”列中的每个示例,每个示例后按 Return/Enter,并查看它们返回的结果。

首先,让我们看看算术运算符,例如

运算符 名称 示例
+ 加法 6 + 9
- 减法 20 - 15
* 乘法 3 * 7
/ 除法 10 / 5

还有一些可用的快捷运算符,称为 复合赋值运算符。例如,如果要将新数字添加到现有数字并返回结果,您可以执行以下操作

js
let number1 = 1;
number1 += 2;

这等效于

js
let number2 = 1;
number2 = number2 + 2;

当我们运行真/假测试(例如在条件语句内部——请参见 下面)时,我们使用 比较运算符。例如

运算符 名称 示例
=== 严格相等(完全相同吗?)
js
5 === 2 + 4 // false
'Chris' === 'Bob' // false
5 === 2 + 3 // true
2 === '2' // false; number versus string
!== 不相等(不相同吗?)
js
5 !== 2 + 4 // true
'Chris' !== 'Bob' // true
5 !== 2 + 3 // false
2 !== '2' // true; number versus string
< 小于
js
6 < 10 // true
20 < 10 // false
> 大于
js
6 > 10 // false
20 > 10 // true

文本字符串

字符串用于表示文本。我们已经看到了一个字符串变量:在以下代码中,"我是一个占位符" 是一个字符串

js
function checkGuess() {
  alert("I am a placeholder");
}

您可以使用双引号 (") 或单引号 (') 声明字符串,但必须对单个字符串声明的开头和结尾使用相同的形式:您不能编写 "我是一个占位符'

您还可以使用反引号 (`) 声明字符串。这样声明的字符串称为模板字面量,具有一些特殊属性。特别是,您可以在其中嵌入其他变量甚至表达式

js
const name = "Mahalia";

const greeting = `Hello ${name}`;

这为您提供了一种将字符串连接在一起的机制。

条件语句

回到我们的 checkGuess() 函数,我认为可以肯定地说我们不希望它只输出一个占位符消息。我们希望它检查玩家的猜测是否正确,并做出相应的响应。

此时,请将您当前的 checkGuess() 函数替换为此版本

js
function checkGuess() {
  const userGuess = Number(guessField.value);
  if (guessCount === 1) {
    guesses.textContent = "Previous guesses:";
  }
  guesses.textContent = `${guesses.textContent} ${userGuess}`;

  if (userGuess === randomNumber) {
    lastResult.textContent = "Congratulations! You got it right!";
    lastResult.style.backgroundColor = "green";
    lowOrHi.textContent = "";
    setGameOver();
  } else if (guessCount === 10) {
    lastResult.textContent = "!!!GAME OVER!!!";
    lowOrHi.textContent = "";
    setGameOver();
  } else {
    lastResult.textContent = "Wrong!";
    lastResult.style.backgroundColor = "red";
    if (userGuess < randomNumber) {
      lowOrHi.textContent = "Last guess was too low!";
    } else if (userGuess > randomNumber) {
      lowOrHi.textContent = "Last guess was too high!";
    }
  }

  guessCount++;
  guessField.value = "";
  guessField.focus();
}

这是很多代码——哇!让我们逐段介绍它的作用。

  • 第一行声明了一个名为 userGuess 的变量,并将其值设置为文本字段中当前输入的值。我们还将此值通过内置的 Number() 构造函数运行,只是为了确保该值绝对是数字。由于我们没有更改此变量,因此我们将使用 const 声明它。
  • 接下来,我们遇到了第一个条件代码块。条件代码块允许您有选择地运行代码,具体取决于某个条件是否为真。它看起来有点像函数,但它不是。最简单的条件块形式以关键字 if 开头,然后是一些括号,然后是一些花括号。在括号内,我们包含一个测试。如果测试返回 true,我们运行花括号内的代码。否则,我们不运行,并继续执行下一段代码。在本例中,测试正在测试 guessCount 变量是否等于 1(即这是否是玩家的第一轮)。
    js
    guessCount === 1;
    
    如果是,我们将猜测段落的文本内容设置为 以前的猜测:。否则,我们不设置。
  • 接下来,我们使用模板字面量将当前 userGuess 值追加到 guesses 段落的末尾,并在两者之间添加一个空格。
  • 下一个块执行了一些检查

    • 第一个 if (){ } 检查用户的猜测是否等于我们 JavaScript 开头设置的 randomNumber。如果是,则玩家猜对了,游戏获胜,因此我们向玩家显示一条带有绿色祝贺消息,清除低/高猜测信息框的内容,并运行一个名为 setGameOver() 的函数,我们稍后将讨论它。
    • 现在,我们使用 else if (){ } 结构将另一个测试链接到最后一个测试的末尾。此测试检查此轮是否为用户的最后一轮。如果是,则程序执行与上一块代码相同的操作,但使用游戏结束消息而不是祝贺消息。
    • 链接到此代码末尾的最后一个块(else { })包含仅在其他两个测试均未返回 true 时(即玩家没有猜对,但还有更多猜测机会)才运行的代码。在这种情况下,我们告诉他们他们错了,然后我们执行另一个条件测试以检查猜测是高于还是低于答案,并根据情况显示更多消息以告诉他们高或低。
  • 函数中的最后三行让我们为提交下一个猜测做好准备。我们将 guessCount 变量加 1,以便玩家用完他们的回合(++ 是一个增量运算符 - 增加 1),并清空表单文本字段中的值并再次聚焦它,准备输入下一个猜测。

事件

此时,我们已经很好地实现了 checkGuess() 函数,但它不会执行任何操作,因为我们还没有调用它。理想情况下,我们希望在按下“提交猜测”按钮时调用它,为此我们需要使用一个**事件**。事件是在浏览器中发生的事情——按钮被点击、页面加载、视频播放等——作为响应,我们可以运行代码块。**事件监听器**观察特定的事件并调用**事件处理程序**,事件处理程序是在事件触发时运行的代码块。

checkGuess() 函数下方添加以下行

js
guessSubmit.addEventListener("click", checkGuess);

在这里,我们向 guessSubmit 按钮添加了一个事件监听器。这是一种采用两个输入值(称为参数)的方法——我们正在监听的事件类型(在本例中为 click)作为字符串,以及事件发生时我们想要运行的代码(在本例中为 checkGuess() 函数)。请注意,在 addEventListener() 中写入时,我们不需要指定括号。

现在尝试保存并刷新您的代码,您的示例应该可以工作——在某种程度上。现在唯一的问题是,如果您猜对了答案或用完了猜测次数,游戏将崩溃,因为我们尚未定义在游戏结束后应该运行的 setGameOver() 函数。让我们现在添加我们缺少的代码并完成示例功能。

完成游戏功能

让我们将 setGameOver() 函数添加到代码的底部,然后逐步了解它。现在将此添加到您的 JavaScript 的其余部分下方

js
function setGameOver() {
  guessField.disabled = true;
  guessSubmit.disabled = true;
  resetButton = document.createElement("button");
  resetButton.textContent = "Start new game";
  document.body.append(resetButton);
  resetButton.addEventListener("click", resetGame);
}
  • 前两行通过将其 disabled 属性设置为 true 来禁用表单文本输入和按钮。这是必要的,因为如果我们不这样做,用户可以在游戏结束后提交更多猜测,这会弄乱事情。
  • 接下来的三行生成一个新的 <button> 元素,将其文本标签设置为“开始新游戏”,并将其添加到我们现有 HTML 的底部。
  • 最后一行在我们的新按钮上设置了一个事件监听器,以便当它被点击时,运行一个名为 resetGame() 的函数。

现在我们也需要定义这个函数!添加以下代码,再次添加到您的 JavaScript 底部

js
function resetGame() {
  guessCount = 1;

  const resetParas = document.querySelectorAll(".resultParas p");
  for (const resetPara of resetParas) {
    resetPara.textContent = "";
  }

  resetButton.parentNode.removeChild(resetButton);

  guessField.disabled = false;
  guessSubmit.disabled = false;
  guessField.value = "";
  guessField.focus();

  lastResult.style.backgroundColor = "white";

  randomNumber = Math.floor(Math.random() * 100) + 1;
}

这段相当长的代码完全重置了游戏开始时的所有内容,以便玩家可以再玩一次。它

  • guessCount 重新设置为 1。
  • 清空所有信息段落中的文本。我们选择 <div class="resultParas"></div> 内的所有段落,然后循环遍历每个段落,将其 textContent 设置为 ''(空字符串)。
  • 从我们的代码中删除重置按钮。
  • 启用表单元素,并清空和聚焦文本字段,准备输入新的猜测。
  • lastResult 段落中删除背景颜色。
  • 生成一个新的随机数,这样你就不会一直猜同一个数字了!

此时,您应该已经拥有了一个完全可运行的(简单的)游戏——恭喜!

现在,在本文中,我们剩下的唯一工作就是讨论您已经看到的一些其他重要代码功能,尽管您可能没有意识到它。

循环

我们需要更详细地查看上述代码的一部分是 for...of 循环。循环是编程中一个非常重要的概念,它允许您一遍又一遍地运行一段代码,直到满足某个条件。

首先,再次转到您的 浏览器开发者工具 JavaScript 控制台,然后输入以下内容

js
const fruits = ["apples", "bananas", "cherries"];
for (const fruit of fruits) {
  console.log(fruit);
}

发生了什么?字符串 'apples', 'bananas', 'cherries' 在您的控制台中打印出来。

这是因为循环。const fruits = ['apples', 'bananas', 'cherries']; 行创建了一个数组。我们将在本模块后面的 完整的数组指南 中详细介绍它,但现在:数组是项目的集合(在本例中为字符串)。

for...of 循环为您提供了一种获取数组中每个项目并在其上运行一些 JavaScript 的方法。for (const fruit of fruits) 行表示

  1. 获取 fruits 中的第一个项目。
  2. fruit 变量设置为该项目,然后运行 {} 花括号之间的代码。
  3. 获取 fruits 中的下一个项目,并重复步骤 2,直到到达 fruits 的末尾。

在本例中,花括号内的代码将 fruit 写入控制台。

现在让我们看看我们数字猜谜游戏中的循环——以下可以在 resetGame() 函数中找到

js
const resetParas = document.querySelectorAll(".resultParas p");
for (const resetPara of resetParas) {
  resetPara.textContent = "";
}

此代码使用 querySelectorAll() 方法创建一个包含 <div class="resultParas"> 内所有段落的列表的变量,然后循环遍历每个段落,删除每个段落的文本内容。

请注意,即使 resetPara 是一个常量,我们也可以更改其内部属性,例如 textContent

关于对象的小讨论

在我们进行此讨论之前,让我们再添加一个最后的改进。在 JavaScript 开头的 let resetButton; 行下方添加以下行,然后保存您的文件

js
guessField.focus();

此行使用 focus() 方法在页面加载后自动将文本光标放入 <input> 文本字段中,这意味着用户可以立即开始键入他们的第一个猜测,而无需先单击表单字段。这只是一个小的补充,但它提高了可用性——为用户提供了有关他们必须执行哪些操作才能玩游戏的良好视觉线索。

让我们更详细地分析一下这里发生了什么。在 JavaScript 中,您将在代码中操作的大多数项目都是对象。对象是存储在单个分组中的相关功能的集合。您可以创建自己的对象,但这相当高级,我们将在课程的后期进行介绍。现在,我们只简要讨论浏览器包含的内置对象,这些对象允许您执行许多有用的操作。

在本例中,我们首先创建了一个 guessField 常量,它存储对 HTML 中文本输入表单字段的引用——以下行可以在我们代码开头的声明中找到

js
const guessField = document.querySelector(".guessField");

为了获取此引用,我们使用了 querySelector() 方法 document 对象。querySelector() 获取一条信息——一个 CSS 选择器,该选择器选择您想要引用到的元素。

因为 guessField 现在包含对 <input> 元素的引用,所以它现在可以访问许多属性(基本上是存储在对象内部的变量,其中一些变量的值不能更改)和方法(基本上是存储在对象内部的函数)。输入元素可以使用的一种方法是 focus(),因此我们现在可以使用此行来聚焦文本输入

js
guessField.focus();

不包含对表单元素引用的变量将无法使用 focus()。例如,guesses 常量包含对 <p> 元素的引用,而 guessCount 变量包含一个数字。

使用浏览器对象

让我们玩一些浏览器对象。

  1. 首先,在浏览器中打开您的程序。
  2. 接下来,打开您的 浏览器开发者工具,并确保 JavaScript 控制台选项卡处于打开状态。
  3. 在控制台中键入 guessField,控制台将显示该变量包含一个 <input> 元素。您还会注意到,控制台会自动完成执行环境中存在的对象(包括您的变量)的名称!
  4. 现在输入以下内容
    js
    guessField.value = 2;
    
    value 属性表示当前输入到文本字段中的值。您将看到通过输入此命令,我们已经更改了文本字段中的文本!
  5. 现在尝试在控制台中键入 guesses 并按 Enter(或 Return,具体取决于您的键盘)。控制台将显示该变量包含一个 <p> 元素。
  6. 现在尝试输入以下行
    js
    guesses.value;
    
    浏览器返回 undefined,因为段落没有 value 属性。
  7. 要更改段落内的文本,您需要使用 textContent 属性。试试这个
    js
    guesses.textContent = "Where is my paragraph?";
    
  8. 现在来做一些有趣的事情。尝试逐一输入以下行
    js
    guesses.style.backgroundColor = "yellow";
    guesses.style.fontSize = "200%";
    guesses.style.padding = "10px";
    guesses.style.boxShadow = "3px 3px 6px black";
    
    页面上的每个元素都有一个 style 属性,该属性本身包含一个对象,其属性包含应用于该元素的所有内联 CSS 样式。这允许我们使用 JavaScript 动态地在元素上设置新的 CSS 样式。

暂时告一段落…

这就是构建示例的全部内容。您已经完成了——做得好!尝试您的最终代码,或 在此处试用我们的完成版本。如果您无法使示例工作,请将其与 源代码 进行比较。