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 樣式。

暫時告一段落…

這就是構建示例的全部內容。您已經完成了——做得好!嘗試您的最終程式碼,或 在此處試用我們的完成版本。如果您無法使示例工作,請將其與 原始碼 進行比較。