初探 JavaScript

現在你已經學習了一些關於 JavaScript 的理論知識以及它的用途,我們將透過一個實際操作教程,引導你瞭解建立簡單 JavaScript 程式的過程。在這裡,你將一步步構建一個簡單的“猜數字”遊戲。

預備知識 瞭解 HTMLCSS 基礎知識
學習成果
  • 像程式設計師一樣思考。
  • 體驗編寫 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 功能。

初始設定

要開始本教程,我們希望你在程式碼編輯器中使用新的 HTML 檔案製作以下程式碼的本地副本。

html
<!doctype html>
<html lang="en-US">
  <head>
    <meta charset="utf-8" />

    <title>Number guessing game</title>

    <style>
      html {
        font-family: sans-serif;
      }

      body {
        width: 50%;
        max-width: 800px;
        min-width: 480px;
        margin: 0 auto;
      }

      .form input[type="number"] {
        width: 200px;
      }

      .lastResult {
        color: white;
        padding: 3px;
      }
    </style>
  </head>

  <body>
    <h1>Number guessing game</h1>

    <p>
      We have selected a random number between 1 and 100. See if you can guess
      it in 10 turns or fewer. We'll tell you if your guess was too high or too
      low.
    </p>

    <div class="form">
      <label for="guessField">Enter a guess: </label>
      <input
        type="number"
        min="1"
        max="100"
        required
        id="guessField"
        class="guessField" />
      <input type="submit" value="Submit guess" class="guessSubmit" />
    </div>

    <div class="resultParas">
      <p class="guesses"></p>
      <p class="lastResult"></p>
      <p class="lowOrHi"></p>
    </div>

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

將其保持在文字編輯器中開啟,並在網路瀏覽器中也開啟它。目前你會看到一個簡單的標題、一段說明和一個用於輸入猜測的表單,但該表單目前不會執行任何操作。

你將把所有 JavaScript 程式碼新增到 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() {
  console.log("I am a placeholder");
}

函式是可重用的程式碼塊,你可以編寫一次並反覆執行,從而避免一遍又一遍地重複程式碼。定義函式有幾種方法,但現在我們將專注於一種簡單的型別。在這裡,我們使用關鍵字 function 後跟一個名稱,並在其後加上括號來定義了一個函式。之後,我們放置了兩個大括號 ({ })。在大括號內部是每當我們呼叫函式時要執行的所有程式碼。

當我們要執行程式碼時,我們輸入函式名稱後跟括號。

我們現在試試。儲存你的程式碼並在瀏覽器中重新整理頁面。然後進入開發者工具 JavaScript 控制檯,並輸入以下行

js
checkGuess();

按下 Return/Enter 後,你應在控制檯中看到 I am a placeholder;我們已在程式碼中定義了一個函式,每當我們呼叫它時,它都會輸出一個佔位符訊息。

文字字串

字串用於表示文字。我們已經見過一個字串變數:在以下程式碼中,"I am a placeholder" 是一個字串

js
function checkGuess() {
  console.log("I am a placeholder");
}

你可以使用雙引號(")或單引號(')宣告字串,但對於單個字串宣告的開頭和結尾,你必須使用相同的形式:你不能寫 "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() 建構函式執行,以確保該值確實是一個數字。

  • 接下來,我們遇到了第一個條件程式碼塊。最簡單的條件塊以關鍵字 if 開頭,然後是一些括號,然後是一些大括號。在括號內部,我們包含一個測試。如果測試返回 true,我們執行大括號內部的程式碼。如果不是,我們不執行,並繼續下一個程式碼段。在這種情況下,我們測試 guessCount 變數是否等於 1(也就是說,這是否是玩家的第一次嘗試)

    js
    guessCount === 1;
    

    如果是,我們將猜測段落的文字內容設定為 Previous guesses:。如果不是,則不設定。

  • 接下來,我們使用模板字面量將當前的 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() 的函式。

現在我們還需要定義 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 段落的背景顏色。
  • 生成一個新的隨機數,這樣你就不會再猜相同的數字了!

至此,你應該擁有一個基本完全正常執行的遊戲——恭喜!

在本文中,我們現在剩下要做的就是討論你已經看到的一些其他重要的程式碼功能,儘管你可能沒有意識到。

迴圈

上面,我們提到了迴圈,這是程式設計中一個非常重要的概念,它允許你重複執行一段程式碼,直到滿足某個條件。

讓我們透過一個基本示例向你展示這意味著什麼。再次前往你的瀏覽器開發者工具 JavaScript 控制檯,將以下程式碼貼上進去,然後按 Enter/Return

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 中的第一個值並將其儲存在一個名為 fruit 的變數中。
  2. 執行 {} 大括號之間的程式碼(在這種情況下,將 fruit 值記錄到控制檯)。
  3. 將下一個陣列值儲存在 fruit 中,並重復步驟 2,直到到達 fruits 陣列的末尾。

現在我們來看看我們猜數字遊戲中的迴圈——以下程式碼可以在 resetGame() 函式中找到

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

此程式碼使用 querySelectorAll() 方法建立一個包含 <div class="resultParas"> 內所有段落的變數,然後遍歷每個段落,刪除其文字內容。

請注意,儘管 resetPara 是一個常量,但我們可以更改其內部屬性,例如 textContent

總結

至此,示例的構建就完成了。你已經到達了終點——幹得好!嘗試你的最終程式碼,或者在這裡玩我們的完成版本。如果你的示例版本無法執行,請對照原始碼進行檢查。

下一課也可能有所幫助——在其中,我們討論了編寫 JavaScript 程式碼時可能出現的問題,同時回顧了“猜數字”遊戲。