出了什麼問題？JavaScript 疑難排解 - 學習該如何開發 Web

出了什麼問題？JavaScript 疑難排解
======================

* 前頁
* Overview: First steps
* 次頁

當你在練習撰寫上一節的「猜數字」遊戲時，你可能會發現它無法運作。不用擔心，本文將會把你從快被拔光的頭髮中拯救出來，並且給你一些小提示，讓你知道怎麼找出及修正 Javascript 的程式運行錯誤。

|  |  |
| --- | --- |
| 先備： | 基本電腦能力，基本html及css理解以及了解JavaScript是什麼 |
| 目標： | 獲得開始解決簡單編碼問題的能力及信心 |

錯誤類型
----

一般來說，當你的編碼有錯誤時，主要有兩種類型

* **語法錯誤**：在編碼中有一些拼字錯誤導致程式完全或部分沒有正常運作。在這個狀況下，通常你會獲得一些錯誤訊息。只要對工具熟悉以及了解錯誤訊息的意思，這種錯誤通常很好修正！
* **邏輯錯誤**：這種錯誤代表程式碼的語法正確，但程式完成的部分不是開發者想做的，意即程式碼執行成功，但返回錯誤的結果。這種錯誤通常比**語法錯誤**還要難修正，因為並沒有錯誤訊息讓我們可以很直接地知道程式碼的問題。

好的，但事情並沒有那麼單純——當你越深入，就會發現更多不同的因素。但上述的分類已經足夠應付初期的工程師職涯了。接著，我們將更深入來討論這兩個類型。

一個錯誤範例
------

讓我們從剛剛的猜數字遊戲開始——在這個版本中，我們將故意引入一些錯誤以便從中學習。前往 Github 下載一份number-game-errors.html（或運行線上版 running live here）。

1. 首先，在編輯器與瀏覽器分別開啟你剛下載檔案。
2. 試著玩遊戲——你會注意到當你按下按鈕「Submit guess」時，它沒有任何反應！

**備註：** 你也許是想修復你自己寫的遊戲中的錯誤！這是件好事，但我們還是建議你在學習這篇文章時先使用我們的版本，這樣你才可以學到我們接下來要教的技巧。在這之後再回去修正你自己的遊戲也不遲！

現在，先讓我們來看看開發者主控台有沒有提示我們任何錯誤，然後試著修正他們。你會在接下來的段落中學到如何修正這些錯誤。

修復語法錯誤
------

在前篇文章中我們讓你在開發者工具 JavaScript console (en-US)中輸入了一些 JavaScript 指令（如果你不記得怎麼打開這個東西，點選前面的連結複習一下）。更重要的是，主控台在瀏覽器的 JavaScript 引擎讀取到有語法錯誤的 JavaScript 時會提示一些錯誤訊息。現在讓我們來看看：

1. 切換到你開啟了`number-game-errors.html` 的分頁，然後打開你的 JavaScript 主控台。你應該會看到如下的幾行錯誤訊息：
 ![](/zh-TW/docs/Learn/JavaScript/First_steps/What_went_wrong/not-a-function.png)
2. 這是一個非常容易追尋的錯誤，而且瀏覽器還給你了不少有用的資訊來幫助你（這張截圖是 Firefox 的，但其他瀏覽器也會提示相似的錯誤訊息）。從左到右，我們可以看到：
    * 一個紅色的「X」代表這是一個錯誤訊息。
    * 一條錯誤訊息提示你什麼東西出錯了：「TypeError: guessSubmit.addeventListener is not a function（TypeError: guessSubmit.addeventListener 並不是一個函式）」
    * 一個「Learn More」連結連向一個解釋這個錯誤並包含大量詳細資訊的 MDN 頁面。
    * 出錯的 JavaScript 檔案名稱，連結連向開發者工具的除錯器。點下這個連結，你就能看到出錯的那行程式被高亮顯示出來。
    * 在該 JavaScript 檔案中錯誤的行號，還有錯誤發生在該行第幾個字元。在這個例子中，是第 86 行的第 3 個字元。
3. 如果我們在編輯器中檢視第 86 行，我們會看到：

```js
guessSubmit.addeventListener("click", checkGuess);
```

4. 主控台提示的錯誤訊息寫著「guessSubmit.addeventListener is not a function（guessSubmit.addeventListener 並不是一個函式）」，所以我們大概是哪裡拼錯字了。如果你並不確定一個函式的正確名稱如何拼寫，打開 MDN 確認看看是個不錯的選擇。最佳做法是在你喜歡的搜尋引擎搜尋「mdn*關鍵字*」。為了節省時間，這裡提供你一個捷徑：`addEventListener()`。
5. 回來看看這個頁面，我們明顯是把函式名稱給拼錯了！記住，JavaScript 是會區分大小寫的，所以任何些微的拼寫錯誤甚至是大小寫錯誤都會造成錯誤發生。把`addeventListener`改成`addEventListener`問題就解決了。現在將你的程式碼修正吧。

**備註：** 看看這個TypeError: "x" is not a function連結來了解更多有關這類錯誤的資訊。

### 語法錯誤：第二回合

1. 將你的頁面存檔並重整，你現在應該會看到剛剛的錯誤消失了。
2. 現在試著輸入一個猜測並按下 Submit guess 按鈕，你會發現...另一個錯誤！
 ![](/zh-TW/docs/Learn/JavaScript/First_steps/What_went_wrong/variable-is-null.png)
3. 這次的錯誤是「TypeError: lowOrHi is null（TypeError: lowOrHi 為 null）」，在第 78 行的位置。

**備註：** `Null`是一個特別的值，代表著「空」、「什麼都沒有」。`lowOrHi`被宣告為一個變數，但並沒有被賦予任何有意義的值——他既沒有變數型態，也沒有值。

**備註：** 這個錯誤並沒有在頁面載入完成後就發生，因為這個錯誤發生在一個函式中（在`checkGuess() { ... }`區塊中）。在之後詳細介紹函式的文章中，你會學到在函式中的程式碼與在函式外的程式碼其實是執行在不同範疇中的。在我們的這個情況裡，有錯誤的程式碼在`checkGuess()`在 86 行被執行前都並沒有執行，也因此錯誤並沒有在頁面一載入就發生。
4. 看看第 78 行，你會看到：

```js
lowOrHi.textContent = "Last guess was too high!";
```

5. 這行試著將`lowOrHi`的`textContent`屬性設為一個字串。但是這行沒有執行成功，因為`lowOrHi`並沒有存著它應該要存著的值。讓我們來看看為什麼——試試在程式碼中搜尋其他`lowOrHi`有出現的地方。在第 48 行你會看到：

```js
var lowOrHi = document.querySelector("lowOrHi");
```

6. 這行程式碼試著將一個 HTML 元素的參照存起來。讓我們檢查一下在這行程式碼執行後，變數中的值是否為`null`。在第 49 行加上：

```js
console.log(lowOrHi);
```

**備註：** `console.log()` (en-US)是一個非常好用的除錯功能，它能夠將值印出至主控台中。所以這行程式碼會在第 48 行賦值給`lowOrHi`後，將它的值印出至主控台中。
7. 存檔並重整，你應該會在主控台中看到`console.log()`輸出的結果。
 ![](/zh-TW/docs/Learn/JavaScript/First_steps/What_went_wrong/console-log-output.png)在這個時間點，`lowOrHi`的值是`null`。所以很明顯的，第 48 行一定出了什麼問題。
8. 讓我們思考一下發生了什麼問題。第 48 行呼叫了 `document.querySelector()` 方法來透過 CSS 選擇器取得一個 HTML 元素參照。打開我們的網頁看看我們想要取得的段落元素：

```js
<p class="lowOrHi"></p>
```

9. 所以我們需要的是一個開頭是小數點(.)的 class 選擇器，但傳進第 48 行`querySelector()`方法的選擇器並沒有開頭的小數點。這也許就是問題所在了！試著將第 48 行的`lowOrHi`改成`.lowOrHi`。
10. 再次存檔並重整，你的`console.log()`現在應該會輸出我們想要的`<p>`元素了。呼！又修好了另一個錯誤！你現在可以把你的`console.log()`那行移除了，或是你想要留著之後查看——取決於你。

**備註：** 看看這個TypeError: "x" is (not) "y" 連結來了解更多有關這類錯誤的資訊。

### 語法錯誤：第三回合

1. 現在如果你試著再次玩這個遊戲應該會相當順利，直到該讓遊戲結束的時機點才會發生錯誤：無論是猜對還是 10 次用完。
2. 此時 console 提供錯誤訊息跟一開始一樣：「TypeError: resetButton.addeventListener is not a function!」 然而此次錯誤來自第 94 行。查看第 94 行後，我們可以輕易發現依舊是屬性大小寫問題，一樣把`addeventListener`改成`.addEventListener`就沒問題了。
邏輯錯誤
----

到這邊為止遊戲應該可以進行得很順利，然而玩幾次下來無疑地你會發現「隨機」數字總是 0 或 1，這可不是我們想要的！

1. 搜尋位於 44 行的變數`randomNumber`，其內容是設定遊戲一開始的隨機數字：

```js
var randomNumber = Math.floor(Math.random()) + 1;
```

2. 另一個開始新回合時產生隨機數字的變數則在 113 行左右：

```js
randomNumber = Math.floor(Math.random()) + 1;
```

3. 為了測試問題是否出在這兩段程式碼，我們需要再次邀請`console.log()` 好朋友，將之分別放到 44、113 行的程式碼下一行：

```js
console.log(randomNumber);
```

4. 儲存程式碼並更新頁面，然後再試玩幾次，你會看到`randomNumber` 在 console 中總是等於 1，這就是問題所在。
### 修正小錯誤

為了修正這個錯誤，我們得先了解它是怎麼運作的。首先，我們呼叫`Math.random()`以產生一個介於 0 到 1 的隨機小數，例如：0.5675493843

```js
Math.random();
```

接著，我們將`Math.random()`產生的隨機小數傳進`Math.floor()`，函式會回傳小於等於所給數字的最大整數，然後為這個整數值加 1：
```

Math.floor(Math.random()) + 1
```

由於`Math.floor`是無條件捨去取整數(地板值)，所以一個介於 0 到 1 的隨機小數永遠只會得到 0，幫這個小數加 1 的話又會永遠只得到 1。所以進位前我們先幫隨機小數乘上 100，如此一來我們就能得到介於 0 到 99 的隨機數字了：

```js
Math.floor(Math.random() \* 100);
```

別忘了還要加上 1，數字範圍才能成功介於 1 到 100：

```js
Math.floor(Math.random() \* 100) + 1;
```

試著自己動手更新這兩行程式碼吧，儲存並更新頁面後你應該能看到遊戲如預期般進行！

其他常見錯誤
------

還有些初學者非常容易忽略的小問題，這小節讓我們來概覽一下：

### 語法錯誤：語句缺少「 ; 」SyntaxError: missing ; before statement

這個錯誤是指每行程式碼結束時必須加上英文輸入法的分號`;`(請注意不要打成中文輸入法)，分號被遺忘的錯誤有時不太容易發現，此外另舉一例：如果我們改動下方變數`checkGuess()` 中的程式碼：

```js
var userGuess = Number(guessField.value);
```

改成

```js
var userGuess === Number(guessField.value);
```

此時程式碼會回報錯誤，因為瀏覽器會認為你想設定不同的東西；我們需要確保自己沒有誤用、混用指派運算子(`=`)：用於賦予變數值跟嚴格比較運算子(`===`)：用於比較兩個值是否完全相等，並回覆`true`/`false`布林值。

**備註：** 更多細節請參考右方關於*缺少分號的語法錯誤*文章頁面： SyntaxError: missing ; before statement (en-US) 。

### 無論輸入什麼，程式總是顯示「你贏了」

還有另一種混用指派運算子(`=`)與嚴格比較運算子(`===`)的狀況，舉例如果我們將變數 `checkGuess()`中的嚴格比較運算子(`===`)

```js
if (userGuess === randomNumber) {
```

改成指派運算子(`=`)

```js
if (userGuess = randomNumber) {
```

這個檢查就失效了，程式會永遠回傳 `true`而勝利並結束遊戲。請小心！

### 語法錯誤：參數列表後面缺少「)」SyntaxError: missing ) after argument list

給完函式或方法參數時別忘了放上`)`右括號（請注意不要打成中文輸入法）。

**備註：** 更多細節請參考右方關於*缺少右括號的語法錯誤*文章頁面：SyntaxError: missing ) after argument list (en-US)。

### 語法錯誤：屬性 ID 後缺少「:」SyntaxError: missing : after property id

這個錯誤通常與 JavaScript 物件格式錯誤有關，在這個例子中，通過修改

```js
function checkGuess() {
```

改成

```js
function checkGuess( {
```

小心括號的部分！這會讓瀏覽器誤以為我們要把函式的程式內容當成函式參數傳入。

### 語法錯誤：函式結尾缺少「}」大括號SyntaxError: missing } after function body

很簡單——這通常意味你的函式或條件結構式缺少一個大括號。若我們將`checkGuess()`函式末段的一個結尾大括號刪除，就會獲得這個錯誤。

### 語法錯誤：預期得到表達式，但實際得到「字串」SyntaxError: expected expression, got '*string*'

或是

### 語法錯誤：字串字面值未正常結束SyntaxError: unterminated string literal

這些錯誤通常意味著你漏寫一個字串起始或結尾的引號，上面第一個錯誤表示漏寫了字串開頭的起始引號，這導致這裡的「*string*」會替換瀏覽器發現的意外字串。第二個錯誤則表示字串結尾缺少了結束引號。

對於這些錯誤，先思考我們在範例中是如何逐項解決的。當出現錯誤資訊時，先查看該行程式碼行號，按照行號到該行程式碼察看，你可以觀察哪裡出錯了。請記住，錯誤不一定出現在該行程式碼上，而且錯誤原因也可能和我們文章上描述的問題不同！

**備註：** 有關這些錯誤的詳細資訊，可參閱SyntaxError: Unexpected token (en-US)與SyntaxError: unterminated string literal (en-US)

小結
--

那麼，我們得到它了——能在簡易 JavaScript 程式中找出錯誤的基礎知識。解決程式碼中的錯誤並不總是那麼簡單，但至少本章節可以為你省下幾小時的時間好用來睡覺，並讓你在早期學習過程更快的解決問題。

另可參閱
----

* 還有許多類型的錯誤未列在此頁，我們正在編輯一份參考資料好詳細解釋了它們的含義——可參閱JavaScript error reference。
* 如果你在閱讀本章節遇到了某些程式錯誤而且不知道如何修正，你可以尋求援助！可以至Learning Area Discourse thread發問，或者到Mozilla IRC的#mdn頻道提問。告訴我們你遇到什麼錯誤，我們會盡力幫助你。若能附上你的程式碼會更有幫助。
* 前頁
* Overview: First steps
* 次頁


有用的字符串方法 - 學習該如何開發 Web

有用的字符串方法
========

* 前頁
* Overview: First steps
* 次頁

現在我們已經了解了字符串的基礎知識，讓我們開始思考我們可以使用內置方法對字符串執行哪些有用的操作，例如查找文本字符串的長度，連接和拆分字符串 ，將字符串中的一個字符替換為另一個字符，等等。

|  |  |
| --- | --- |
| 先備知識: | 基礎的電腦素養、基本的HTML和CSS、以及清楚什麼是JavaScript。 |
| 目標: | 了解字串是物件，學習使用一些能夠應用這些字串的基礎方法。 |

把字串當作物件
-------

我們曾經說過，現在我們重申一遍—在 javascript 中，一切東西都可以被當作物件。例如我們創建一個字串。

```js
var string = "This is my string";
```

你的變數成為一個字串的實體物件，因此它將有許多性質(properties)與功能(methods)可以使用。

你的變數成為一個字串的實體物件，因此它將有許多性質(properties)與功能(methods)可以使用。你可以到 `String` 物件頁面的左方列表查看這些性質與功能!

\*\*好的，在你腦袋燒壞之前先別擔心！\*\*在這趟學習旅程中，關於這些大部分對於現在的你其實還不需要知道。不過有一些你可能會經常使用，我們將在這裡介紹。

Let's enter some examples into a fresh console. We've provided one below (you can also open this console in a separate tab or window, or use the browser developer console (en-US) if you'd prefer).
```

<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>JavaScript console</title>
    <style>
 \* {
 box-sizing: border-box;
 }

 html {
 background-color: #0c323d;
 color: #809089;
 font-family: monospace;
 }

 body {
 max-width: 700px;
 }

 p {
 margin: 0;
 width: 1%;
 padding: 0 1%;
 font-size: 16px;
 line-height: 1.5;
 float: left;
 }

 .input p {
 margin-right: 1%;
 }

 .output p {
 width: 100%;
 }

 .input input {
 width: 96%;
 float: left;
 border: none;
 font-size: 16px;
 line-height: 1.5;
 font-family: monospace;
 padding: 0;
 background: #0c323d;
 color: #809089;
 }

 div {
 clear: both;
 }
 </style>
  </head>
  <body></body>

  <script>
 var geval = eval;
 function createInput() {
 var inputDiv = document.createElement("div");
 var inputPara = document.createElement("p");
 var inputForm = document.createElement("input");

 inputDiv.setAttribute("class", "input");
 inputPara.textContent = ">";
 inputDiv.appendChild(inputPara);
 inputDiv.appendChild(inputForm);
 document.body.appendChild(inputDiv);

 inputForm.addEventListener("change", executeCode);
 }

 function executeCode(e) {
 try {
 var result = geval(e.target.value);
 } catch (e) {
 var result = "error — " + e.message;
 }

 var outputDiv = document.createElement("div");
 var outputPara = document.createElement("p");

 outputDiv.setAttribute("class", "output");
 outputPara.textContent = "Result: " + result;
 outputDiv.appendChild(outputPara);
 document.body.appendChild(outputDiv);

 e.target.disabled = true;
 e.target.parentNode.style.opacity = "0.5";

 createInput();
 }

 createInput();
 </script>
</html>
```

### 找出字串的長度（length）

這很簡單，你可以用 `length` (en-US) 屬性。試著輸入下面幾行：

```js
var browserType = "mozilla";
browserType.length;
```

結果應該會回傳數字 7，因為 "mozilla" 字元長度是 7。 這在很多狀況下很好用，舉例來說：你會想知道序列的長度，這樣才能將這些序列按照長度排序，或是讓使用者知道他們輸入的名稱是否太長。

### 取得字串中的特定字元（string character）

On a related note, you can return any character inside a string by using **square bracket notation** — this means you include square brackets (`[]`) on the end of your variable name. Inside the square brackets you include the number of the character you want to return, so for example to retrieve the first letter you'd do this:

```js
browserType[0];
```

記得電腦計數從 0 開始，不是 1！ 如果要在*任何*一個字串中取得最後一個字元，我們可以使用以下程式碼，結合了取得字元的技巧和上面學過的長度屬性：

```js
browserType[browserType.length - 1];
```

"mozilla" 這個詞的長度是 7，但因為電腦是從 0 開始計數，所以最後一個位置是 6，因此我們會將 `length-1` 。你也可以試試用這個方法找各序列的第一個字母，並將這些序列按字母順序排好 。

### 尋找字串中的子字串（substring）並提出子字串

1. Sometim 有時候你會想搜尋是否有一個較小的字串存在於比較大的字串中（*我們通常會說是否有個子字串存在於字串中*）。這可以用 `indexOf()` (en-US) 方法，當中需要一個參數（ parameter ），也就是你想搜尋的子字串：

```js
browserType.indexOf("zilla");
```

結果會傳回 2，因為子字串 "zilla" 在 "mozilla" 中是從位置 2 開始的。（依然要記得電腦計數是從 0 開始）。這個方法可以用篩選字串，舉例來說：我們有一串網址的清單，而我們只想印出那些包含 "mozilla" 的網址。
2. This can be done in another way, which is possibly even more effective. Try the following:

```js
browserType.indexOf("vanilla");
```

This should give you a result of `-1` — this is returned when the substring, in this case 'vanilla', is not found in the main string.
 You could use this to find all instances of strings that **don't** contain the substring 'mozilla', or **do,** if you use the negation operator, as shown below. You could do something like this:

```js
if (browserType.indexOf("mozilla") !== -1) {
  // do stuff with the string
}
```

3. When you know where a substring starts inside a string, and you know at which character you want it to end, `slice()` (en-US) can be used to extract it. Try the following:

```js
browserType.slice(0, 3);
```

This returns "moz" — the first parameter is the character position to start extracting at, and the second parameter is the character position after the last one to be extracted. So the slice happens from the first position, up to, but not including, the last position. In this example, since the starting index is 0, the second parameter is equal to the length of the string being returned.
4. Also, if you know that you want to extract all of the remaining characters in a string after a certain character, you don't have to include the second parameter! Instead, you only need to include the character position from where you want to extract the remaining characters in a string. Try the following:

```js
browserType.slice(2);
```

This returns "zilla" — this is because the character position of 2 is the letter z, and because you didn't include a second parameter, the substring that was returned was all of the remaining characters in the string.

**備註：** The second parameter of `slice()` is optional: if you don't include it, the slice ends at the end of the original string. There are other options too; study the `slice()` (en-US) page to see what else you can find out.

### 改變大小寫

The string methods `toLowerCase()` and `toUpperCase()` (en-US) take a string and convert all the characters to lower- or uppercase, respectively. This can be useful for example if you want to normalize all user-entered data before storing it in a database.

Let's try entering the following lines to see what happens:

```js
var radData = "My NaMe Is MuD";
radData.toLowerCase();
radData.toUpperCase();
```

### 更動部分字串

You can replace one substring inside a string with another substring using the `replace()` (en-US) method. This works very simply at a basic level, although there are some advanced things you can do with it that we won't go into yet.

It takes two parameters — the string you want to replace, and the string you want to replace it with. Try this example:

```js
browserType.replace("moz", "van");
```

Note that to actually get the updated value reflected in the `browserType` variable in a real program, you'd have to set the variable value to be the result of the operation; it doesn't just update the substring value automatically. So you'd have to actually write this: `browserType = browserType.replace('moz','van');`

Active learning examples
------------------------

In this section we'll get you to try your hand at writing some string manipulation code. In each exercise below, we have an array of strings, and a loop that processes each value in the array and displays it in a bulleted list. You don't need to understand arrays or loops right now — these will be explained in future articles. All you need to do in each case is write the code that will output the strings in the format that we want them in.

Each example comes with a "Reset" button, which you can use to reset the code if you make a mistake and can't get it working again, and a "Show solution" button you can press to see a potential answer if you get really stuck.

### Filtering greeting messages

In the first exercise we'll start you off simple — we have an array of greeting card messages, but we want to sort them to list just the Christmas messages. We want you to fill in a conditional test inside the `if( ... )` structure, to test each string and only print it in the list if it is a Christmas message.

1. First think about how you could test whether the message in each case is a Christmas message. What string is present in all of those messages, and what method could you use to test whether it is present?
2. You'll then need to write a conditional test of the form *operand1 operator operand2*. Is the thing on the left equal to the thing on the right? Or in this case, does the method call on the left return the result on the right?
3. Hint: In this case it is probably more useful to test whether the method call *isn't* equal to a certain result.
```

<h2>Live output</h2>

<div class="output" style="min-height: 125px;">
  <ul></ul>
</div>

<h2>Editable code</h2>
<p class="a11y-label">
  Press Esc to move focus away from the code area (Tab inserts a tab character).
</p>

<textarea id="code" class="playable-code" style="height: 290px; width: 95%">
var list = document.querySelector('.output ul');
list.innerHTML = '';
var greetings = ['Happy Birthday!',
                 'Merry Christmas my love',
                 'A happy Christmas to all the family',
                 'You\'re all I want for Christmas',
                 'Get well soon'];

for (var i = 0; i < greetings.length; i++) {
  var input = greetings[i];
  // Your conditional test needs to go inside the parentheses
  // in the line below, replacing what's currently there
  if (greetings[i]) {
    var result = input;
    var listItem = document.createElement('li');
    listItem.textContent = result;
    list.appendChild(listItem);
  }
}
</textarea>

<div class="playable-buttons">
  <input id="reset" type="button" value="Reset" />
  <input id="solution" type="button" value="Show solution" />
</div>
```

```
html {
  font-family: sans-serif;
}

h2 {
  font-size: 16px;
}

.a11y-label {
  margin: 0;
  text-align: right;
  font-size: 0.7rem;
  width: 98%;
}

body {
  margin: 10px;
  background: #f5f9fa;
}
```

```
var textarea = document.getElementById("code");
var reset = document.getElementById("reset");
var solution = document.getElementById("solution");
var code = textarea.value;
var userEntry = textarea.value;

function updateCode() {
  eval(textarea.value);
}

reset.addEventListener("click", function () {
  textarea.value = code;
  userEntry = textarea.value;
  solutionEntry = jsSolution;
  solution.value = "Show solution";
  updateCode();
});

solution.addEventListener("click", function () {
  if (solution.value === "Show solution") {
    textarea.value = solutionEntry;
    solution.value = "Hide solution";
  } else {
    textarea.value = userEntry;
    solution.value = "Show solution";
  }
  updateCode();
});

var jsSolution =
  "var list = document.querySelector('.output ul');\nlist.innerHTML = '';\nvar greetings = ['Happy Birthday!',\n 'Merry Christmas my love',\n 'A happy Christmas to all the family',\n 'You\\'re all I want for Christmas',\n 'Get well soon'];\n\nfor(var i = 0; i < greetings.length; i++) {\n var input = greetings[i];\n if(greetings[i].indexOf('Christmas') !== -1) {\n var result = input;\n var listItem = document.createElement('li');\n listItem.textContent = result;\n list.appendChild(listItem);\n }\n}";
var solutionEntry = jsSolution;

textarea.addEventListener("input", updateCode);
window.addEventListener("load", updateCode);

// stop tab key tabbing out of textarea and
// make it write a tab at the caret position instead

textarea.onkeydown = function (e) {
  if (e.keyCode === 9) {
    e.preventDefault();
    insertAtCaret("\t");
  }

  if (e.keyCode === 27) {
    textarea.blur();
  }
};

function insertAtCaret(text) {
  var scrollPos = textarea.scrollTop;
  var caretPos = textarea.selectionStart;

  var front = textarea.value.substring(0, caretPos);
  var back = textarea.value.substring(
    textarea.selectionEnd,
    textarea.value.length,
  );
  textarea.value = front + text + back;
  caretPos = caretPos + text.length;
  textarea.selectionStart = caretPos;
  textarea.selectionEnd = caretPos;
  textarea.focus();
  textarea.scrollTop = scrollPos;
}

// Update the saved userCode every time the user updates the text area code

textarea.onkeyup = function () {
  // We only want to save the state when the user code is being shown,
  // not the solution, so that solution is not saved over the user code
  if (solution.value === "Show solution") {
    userEntry = textarea.value;
  } else {
    solutionEntry = textarea.value;
  }

  updateCode();
};
```

### Fixing capitalization

In this exercise we have the names of cities in the United Kingdom, but the capitalization is all messed up. We want you to change them so that they are all lower case, except for a capital first letter. A good way to do this is to:

1. Convert the whole of the string contained in the `input` variable to lower case and store it in a new variable.
2. Grab the first letter of the string in this new variable and store it in another variable.
3. Using this latest variable as a substring, replace the first letter of the lowercase string with the first letter of the lowercase string changed to upper case. Store the result of this replace procedure in another new variable.
4. Change the value of the `result` variable to equal to the final result, not the `input`.

**備註：** A hint — the parameters of the string methods don't have to be string literals; they can also be variables, or even variables with a method being invoked on them.
```

<h2>Live output</h2>

<div class="output" style="min-height: 125px;">
  <ul></ul>
</div>

<h2>Editable code</h2>
<p class="a11y-label">
  Press Esc to move focus away from the code area (Tab inserts a tab character).
</p>

<textarea id="code" class="playable-code" style="height: 250px; width: 95%">
var list = document.querySelector('.output ul');
list.innerHTML = '';
var cities = ['lonDon', 'ManCHESTer', 'BiRmiNGHAM', 'liVERpoOL'];
for(var i = 0; i < cities.length; i++) {
  var input = cities[i];
  // write your code just below here

  var result = input;
  var listItem = document.createElement('li');
  listItem.textContent = result;
  list.appendChild(listItem);
}
</textarea>

<div class="playable-buttons">
  <input id="reset" type="button" value="Reset" />
  <input id="solution" type="button" value="Show solution" />
</div>
```

```
html {
  font-family: sans-serif;
}

h2 {
  font-size: 16px;
}

.a11y-label {
  margin: 0;
  text-align: right;
  font-size: 0.7rem;
  width: 98%;
}

body {
  margin: 10px;
  background: #f5f9fa;
}
```

```
var textarea = document.getElementById("code");
var reset = document.getElementById("reset");
var solution = document.getElementById("solution");
var code = textarea.value;
var userEntry = textarea.value;

function updateCode() {
  eval(textarea.value);
}

reset.addEventListener("click", function () {
  textarea.value = code;
  userEntry = textarea.value;
  solutionEntry = jsSolution;
  solution.value = "Show solution";
  updateCode();
});

solution.addEventListener("click", function () {
  if (solution.value === "Show solution") {
    textarea.value = solutionEntry;
    solution.value = "Hide solution";
  } else {
    textarea.value = userEntry;
    solution.value = "Show solution";
  }
  updateCode();
});

var jsSolution =
  "var list = document.querySelector('.output ul');\nlist.innerHTML = '';\nvar cities = ['lonDon', 'ManCHESTer', 'BiRmiNGHAM', 'liVERpoOL'];\n\nfor(var i = 0; i < cities.length; i++) {\n var input = cities[i];\n var lower = input.toLowerCase();\n var firstLetter = lower.slice(0,1);\n var capitalized = lower.replace(firstLetter,firstLetter.toUpperCase());\n var result = capitalized;\n var listItem = document.createElement('li');\n listItem.textContent = result;\n list.appendChild(listItem);\n\n}";
var solutionEntry = jsSolution;

textarea.addEventListener("input", updateCode);
window.addEventListener("load", updateCode);

// stop tab key tabbing out of textarea and
// make it write a tab at the caret position instead

textarea.onkeydown = function (e) {
  if (e.keyCode === 9) {
    e.preventDefault();
    insertAtCaret("\t");
  }

  if (e.keyCode === 27) {
    textarea.blur();
  }
};

function insertAtCaret(text) {
  var scrollPos = textarea.scrollTop;
  var caretPos = textarea.selectionStart;

  var front = textarea.value.substring(0, caretPos);
  var back = textarea.value.substring(
    textarea.selectionEnd,
    textarea.value.length,
  );
  textarea.value = front + text + back;
  caretPos = caretPos + text.length;
  textarea.selectionStart = caretPos;
  textarea.selectionEnd = caretPos;
  textarea.focus();
  textarea.scrollTop = scrollPos;
}

// Update the saved userCode every time the user updates the text area code

textarea.onkeyup = function () {
  // We only want to save the state when the user code is being shown,
  // not the solution, so that solution is not saved over the user code
  if (solution.value === "Show solution") {
    userEntry = textarea.value;
  } else {
    solutionEntry = textarea.value;
  }

  updateCode();
};
```

### Making new strings from old parts

In this last exercise, the array contains a bunch of strings containing information about train stations in the North of England. The strings are data items that contain the three-letter station code, followed by some machine-readable data, followed by a semicolon, followed by the human-readable station name. For example:
```

MAN675847583748sjt567654;Manchester Piccadilly
```

We want to extract the station code and name, and put them together in a string with the following structure:
```

MAN: Manchester Piccadilly
```

We'd recommend doing it like this:

1. Extract the three-letter station code and store it in a new variable.
2. Find the character index number of the semicolon.
3. Extract the human-readable station name using the semicolon character index number as a reference point, and store it in a new variable.
4. Concatenate the two new variables and a string literal to make the final string.
5. Change the value of the `result` variable to equal to the final string, not the `input`.
```

<h2>Live output</h2>

<div class="output" style="min-height: 125px;">
  <ul></ul>
</div>

<h2>Editable code</h2>
<p class="a11y-label">
  Press Esc to move focus away from the code area (Tab inserts a tab character).
</p>

<textarea id="code" class="playable-code" style="height: 285px; width: 95%">
var list = document.querySelector('.output ul');
list.innerHTML = '';
var stations = ['MAN675847583748sjt567654;Manchester Piccadilly',
                'GNF576746573fhdg4737dh4;Greenfield',
                'LIV5hg65hd737456236dch46dg4;Liverpool Lime Street',
                'SYB4f65hf75f736463;Stalybridge',
                'HUD5767ghtyfyr4536dh45dg45dg3;Huddersfield'];

for (var i = 0; i < stations.length; i++) {
  var input = stations[i];
  // write your code just below here

  var result = input;
  var listItem = document.createElement('li');
  listItem.textContent = result;
  list.appendChild(listItem);
}
</textarea>

<div class="playable-buttons">
  <input id="reset" type="button" value="Reset" />
  <input id="solution" type="button" value="Show solution" />
</div>
```

```
html {
  font-family: sans-serif;
}

h2 {
  font-size: 16px;
}

.a11y-label {
  margin: 0;
  text-align: right;
  font-size: 0.7rem;
  width: 98%;
}

body {
  margin: 10px;
  background: #f5f9fa;
}
```

```
var textarea = document.getElementById("code");
var reset = document.getElementById("reset");
var solution = document.getElementById("solution");
var code = textarea.value;
var userEntry = textarea.value;

function updateCode() {
  eval(textarea.value);
}

reset.addEventListener("click", function () {
  textarea.value = code;
  userEntry = textarea.value;
  solutionEntry = jsSolution;
  solution.value = "Show solution";
  updateCode();
});

solution.addEventListener("click", function () {
  if (solution.value === "Show solution") {
    textarea.value = solutionEntry;
    solution.value = "Hide solution";
  } else {
    textarea.value = userEntry;
    solution.value = "Show solution";
  }
  updateCode();
});

var jsSolution =
  "var list = document.querySelector('.output ul');\nlist.innerHTML = '';\nvar stations = ['MAN675847583748sjt567654;Manchester Piccadilly',\n 'GNF576746573fhdg4737dh4;Greenfield',\n 'LIV5hg65hd737456236dch46dg4;Liverpool Lime Street',\n 'SYB4f65hf75f736463;Stalybridge',\n 'HUD5767ghtyfyr4536dh45dg45dg3;Huddersfield'];\n\nfor(var i = 0; i < stations.length; i++) {\n var input = stations[i];\n var code = input.slice(0,3);\n var semiC = input.indexOf(';');\n var name = input.slice(semiC + 1);\n var result = code + ': ' + name;\n var listItem = document.createElement('li');\n listItem.textContent = result;\n list.appendChild(listItem);\n}";
var solutionEntry = jsSolution;

textarea.addEventListener("input", updateCode);
window.addEventListener("load", updateCode);

// stop tab key tabbing out of textarea and
// make it write a tab at the caret position instead

textarea.onkeydown = function (e) {
  if (e.keyCode === 9) {
    e.preventDefault();
    insertAtCaret("\t");
  }

  if (e.keyCode === 27) {
    textarea.blur();
  }
};

function insertAtCaret(text) {
  var scrollPos = textarea.scrollTop;
  var caretPos = textarea.selectionStart;

  var front = textarea.value.substring(0, caretPos);
  var back = textarea.value.substring(
    textarea.selectionEnd,
    textarea.value.length,
  );
  textarea.value = front + text + back;
  caretPos = caretPos + text.length;
  textarea.selectionStart = caretPos;
  textarea.selectionEnd = caretPos;
  textarea.focus();
  textarea.scrollTop = scrollPos;
}

// Update the saved userCode every time the user updates the text area code

textarea.onkeyup = function () {
  // We only want to save the state when the user code is being shown,
  // not the solution, so that solution is not saved over the user code
  if (solution.value === "Show solution") {
    userEntry = textarea.value;
  } else {
    solutionEntry = textarea.value;
  }

  updateCode();
};
```

結語
--

不可否認當網站在跟人們互相溝通時，處理文字和句子在程式設計中是相當重要的，尤其是在 JavaScript 中。這篇文章已經傳授你如何去處理字串的方法，應該對以後深入了解其他更複雜主題的你會很有幫助。接下來，我們將會看看最後一個近期內我們需要關注的主要的資料型態 — 陣列。

* 前頁
* Overview: First steps
* 次頁


存儲你需要的資訊 - 變數 - 學習該如何開發 Web

存儲你需要的資訊 - 變數
=============

* 前頁
* Overview: First steps
* 次頁

閱讀完最後幾篇文章之後，你現在應該知道 JavaScript 是什麼，它可以為你做什麼，如何將它與其他 Web 技術一起使用，以及它的主要功能從高層看起來如何。 在本文中，我們將深入了解真正的基礎知識，了解如何使用 JavaScript 的大多數基本構建塊 - 變數。

|  |  |
| --- | --- |
| 必備知識: | 電腦基礎知識，了解基本的 HTML 和 CSS ，了解 JavaScript 是什麼。 |
| 目標: | 熟悉 JavaScript 變數的基本知識。 |

你需要的工具
------

在此篇文章中，你將被要求輸入程式碼行來測試你對內容的理解。如果你使用的是網頁瀏覽器，最適合輸入代碼的地方便是 JavaScript 主控台, (請參閱什麼是瀏覽器開發工具 (en-US)這篇文章以取得更多關於此工具的資訊).

什麼是變量／變數 (variable) ？
---------------------

變量是值的容器，就像我們可能在總和中使用的數字，或者我們可能用作句子一部分的字符串。 但關於變量的一個特殊之處在於它們包含的值可以改變。 我們來看一個簡單的例子：

```html
<button>請按我</button>
```

```js
const button = document.querySelector("button");

button.onclick = function () {
  let name = prompt("你叫什麼名字?");
  alert("你好 " + name + ", 很高興認識你!");
};
```

在此示例中，按下按鈕會運行幾行代碼。 第一行在屏幕上彈出一個框，要求讀者輸入其名稱，然後將值存儲在變量中。 第二行顯示歡迎消息，其中包含從變量值中獲取的名稱。

要理解為什麼這麼有用，讓我們考慮如何在不使用變量的情況下編寫此示例。 它最終會看起來像這樣：
```

let name = prompt('What is your name?');

if (name === 'Adam') {
  alert('Hello Adam, nice to see you!');
} else if (name === 'Alan') {
  alert('Hello Alan, nice to see you!');
} else if (name === 'Bella') {
  alert('Hello Bella, nice to see you!');
} else if (name === 'Bianca') {
  alert('Hello Bianca, nice to see you!');
} else if (name === 'Chris') {
  alert('Hello Chris, nice to see you!');
}

// ... 等等 ...
```

你可能暫時還沒有完全理解這些代碼和語法，但是你應該能夠理解到 — 如果我們沒有變量，我們就不得不寫大量的代碼去檢查輸入的名字，然後顯示相應名稱的消息 。這樣做顯然是低效率（雖然例子中只有 5 種選擇，但代碼卻相對地長）和不可行的 — 你沒有辦法列舉出所有可能的名字。

使用變量才是明智的。隨著你對 JavaScript 越來越了解，你會開始習慣使用變量。

變量的另一個特性就是它們能夠存儲任何的東西 — 不只是字符串和數字。變量可以存儲更複雜的數據，甚至是函數。你將在後續的內容中體驗到這些用法。

**備註：** 變量是用來儲存數值的，而變量和數值是兩個不同的概念。變量不是數值本身，它們僅僅是一個用於儲存數值的容器。你可以把變量想像成一個個用來裝東西的紙皮箱。

![](/zh-TW/docs/Learn/JavaScript/First_steps/Variables/boxes.png)

定義變量 (Declaring a variable)
---------------------------

要想使用變量，你需要做的第一步就是創建它 — 更準確的說，是定義一個變量。定義一個變量的語法是在關鍵字 `var` 或 `let` 之後加上變量的名字：

```js
let myName;
let myAge;
```

在這裡我們定義了兩個變量 `myName` 和 `myAge`。那麼現在嘗試輸入這些代碼到你的瀏覽器終端。之後，嘗試使用你自己選擇的名稱來創建一兩個變量。

**備註：** 在 JavaScript 中，所有代碼指令都會以分號結尾（ `;`）－ 如果忘記加分號，你的單行代碼可能正常執行，但是在執行多行代碼的時候就可能出錯。所以，最好是養成主動以分號作為代碼結尾的習慣。

你可以輸入變量的名稱，來驗證這個變量的數值是否在執行環境（execution environment）中已經存在。例如，

```js
myName;
myAge;
```

以上這兩個變量並沒有數值，他們是空的容器。當你輸入變量名並按輸入鍵後，你會得到一個 `undefined` （沒有定義的值）的返回值。如果變量並不存在的話，你就會得到一個錯誤信息。請嘗試輸入：

```js
scoobyDoo;
```

**備註：** 千萬不要把兩個概念弄混淆了，「一個變量**存在，但是沒有數值**」和「一個變量**並不存在**」— 他們完全是兩回事。在前面你看到的盒子的類比中，不存在意味著沒有可以存放變量的「盒子」。沒有定義的值意味著**有**一個「盒子」，但是它裡面沒有任何數值。

初始化變量 (Initializing a variable)
-------------------------------

一旦你定義了一個變量，你就能夠初始化它來儲存數值。方法如下：在變量名之後跟上一個等號 (`=`)，然後是數值。例如：

```js
myName = "Chris";
myAge = 37;
```

Try going back to the console now and typing in these lines. You should see the value you've assigned to the variable returned in the console to confirm it, in each case. Again, you can return your variable values by simply typing their name into the console — try these again:

```js
myName;
myAge;
```

你可以同時定義並初始化變量，像是：

```js
let myDog = "Rover";
```

This is probably what you'll do most of the time, as it is quicker than doing the two actions on two separate lines.

比較 var 和 let 的不同 (The difference between var and let)
-----------------------------------------------------

此刻你或許會思考「為什麼我們需要兩種方法來定義變數??」「為甚麼要有`var`和`let`??」

原因有些歷史淵源。在 Javascript 剛被創造的時候，只有`var`可以使用。在大部分的情況下都很正常，但是`var`的運作方式有些問題 — 它的設計有時會令人困惑甚至惹惱人。所以`let`在現代版本中的 Javascript 被創造出來，一個與`var`工作原理有些不同的創建變數的關鍵字，修復了`var`的種種問題。

以下將介紹幾個簡單的分別。我們現在不會一一講解全部的不同，但是當你慢慢深入 Javascript，你將會開始發現它們的(如果你真的很想現在知道，歡迎看看我們的let 介紹頁)。

如下，假設你需要宣告、初始化一個變數`myName`，即使你是初始化之後才宣告也是可行的：

```js
myName = "Chris";

function logName() {
  console.log(myName);
}

logName();

var myName;
```

**備註：** This won't work when typing individual lines into a JavaScript console, just when running multiple lines of JavaScript in a web document.

This works because of **hoisting** — read var hoisting for more detail on the subject.

Hoisting no longer works with `let`. If we changed `var` to `let` in the above example, it would fail with an error. This is a good thing — declaring a variable after you initialize it makes for confusing, harder to understand code.

Secondly, when you use `var`, you can declare the same variable as many times as you like, but with `let` you can't. The following would work:

```js
var myName = "Chris";
var myName = "Bob";
```

But the following would throw an error on the second line:

```js
let myName = "Chris";
let myName = "Bob";
```

You'd have to do this instead:

```js
let myName = "Chris";
myName = "Bob";
```

Again, this is a sensible language decision. There is no reason to redeclare variables — it just makes things more confusing.

For these reasons and more, we recommend that you use `let` as much as possible in your code, rather than `var`. There is no reason to use `var`, unless you need to support old versions of Internet Explorer with your code (it doesn't support `let` until version 11; the modern Windows Edge browser supports `let` just fine).

**備註：** We are currently in the process of updating the course to use `let` rather than `var`. Bear with us!

Updating a variable
-------------------

Once a variable has been initialized with a value, you can change (or update) that value by simply giving it a different value. Try entering the following lines into your console:

```js
myName = "Bob";
myAge = 40;
```

### 變數命名規則悄悄話

You can call a variable pretty much anything you like, but there are limitations. Generally, you should stick to just using Latin characters (0-9, a-z, A-Z) and the underscore character.

* You shouldn't use other characters because they may cause errors or be hard to understand for an international audience.
* Don't use underscores at the start of variable names — this is used in certain JavaScript constructs to mean specific things, so may get confusing.
* Don't use numbers at the start of variables. This isn't allowed and will cause an error.
* A safe convention to stick to is so-called "lower camel case", where you stick together multiple words, using lower case for the whole first word and then capitalize subsequent words. We've been using this for our variable names in the article so far.
* Make variable names intuitive, so they describe the data they contain. Don't just use single letters/numbers, or big long phrases.
* Variables are case sensitive — so `myage` is a different variable to `myAge`.
* One last point — you also need to avoid using JavaScript reserved words as your variable names — by this, we mean the words that make up the actual syntax of JavaScript! So, you can't use words like `var`, `function`, `let`, and `for` as variable names. Browsers will recognize them as different code items, and so you'll get errors.

**備註：** You can find a fairly complete list of reserved keywords to avoid at Lexical grammar — keywords.

良好的命名範例：
```

age
myAge
init
initialColor
finalOutputValue
audio1
audio2
```

不好的命名範例：
```

1
a
_12
myage
MYAGE
var
Document
skjfndskjfnbdskjfb
thisisareallylongstupidvariablenameman
```

Error-prone name examples:
```

var
Document
```

Try creating a few more variables now, with the above guidance in mind.

變數資料類型
------

There are a few different types of data we can store in variables. In this section we'll describe these in brief, then in future articles, you'll learn about them in more detail.

So far we've looked at the first two, but there are others.

### Numbers 數字

You can store numbers in variables, either whole numbers like 30 (also called integers) or decimal numbers like 2.456 (also called floats or floating point numbers). You don't need to declare variable types in JavaScript, unlike some other programming languages. When you give a variable a number value, you don't include quotes:

```js
let myAge = 17;
```

### Strings 字串

Strings are pieces of text. When you give a variable a string value, you need to wrap it in single or double quote marks, otherwise, JavaScript will try to interpret it as another variable name.

```js
let dolphinGoodbye = "So long and thanks for all the fish";
```

### Booleans 布林值

Booleans are true/false values — they can have two values, `true` or `false`. These are generally used to test a condition, after which code is run as appropriate. So for example, a simple case would be:

```js
let iAmAlive = true;
```

Whereas in reality it would be used more like this:

```js
let test = 6 < 3;
```

This is using the "less than" operator (`<`) to test whether 6 is less than 3. As you might expect, it will return `false`, because 6 is not less than 3! You will learn a lot more about such operators later on in the course.

### Arrays 陣列

An array is a single object that 它包含多個用方括號括起來並用逗號分隔的值。Try entering the following lines into your console:

```js
let myNameArray = ["Chris", "Bob", "Jim"];
let myNumberArray = [10, 15, 40];
```

Once these arrays are defined, you can access each value by their location within the array. Try these lines:

```js
myNameArray[0]; // should return 'Chris'
myNumberArray[2]; // should return 40
```

The square brackets specify an index value corresponding to the position of the value you want returned. You might have noticed that arrays in JavaScript are zero-indexed: the first element is at index 0.

You'll learn a lot more about arrays in a future article.

### Objects 物件

在編程中，物件是對真實生活物件進行建模的代碼結構。 你可以擁有一個代表停車場的簡單物件，其中包含有關其寬度和長度的信息，或者你可以擁有一個代表一個人的物件，並用物件紀錄其姓名、身高、體重、慣用語言，如何跟他打招呼等的資訊。

請試著在你的 console 中輸入以下指令：

```js
let dog = { name: "Spot", breed: "Dalmatian" };
```

取得物件中儲存的資料可以使用以下語法：

```js
dog.name;
```

We won't be looking at objects any more for now — you can learn more about those in a future module.

動態型別
----

JavaScript 是一個"動態型別語言"，意思是不像其他強型別程式語言，在 JavaScript 宣告變數時你不用指定指定資料類型(數字、字串或陣列等等)。

For example, if you declare a variable and give it a value enclosed in quotes, the browser will treat the variable as a string:

```js
let myString = "Hello";
```

It will still be a string, even if it contains numbers, so be careful:

```js
let myNumber = "500"; // oops, this is still a string
typeof myNumber;
myNumber = 500; // much better — now this is a number
typeof myNumber;
```

Try entering the four lines above into your console one by one, and see what the results are. You'll notice that we are using a special operator called `typeof` — this returns the data type of the variable you pass into it. The first time it is called, it should return `string`, as at that point the `myNumber` variable contains a string, `'500'`. Have a look and see what it returns the second time you call it.

JavaScript 中的常數
---------------

許多程式語言都有常數的概念：一經宣告就不改變的值。設定常數有許多原因，例如若引入第三方腳本而改動變數值，將會造成許多問題而且很難除錯。

一開始 JavaScript 是沒有常數的，直到今日我們才有了關鍵字 `const`，讓我們儲存不能改變的值：

```js
const daysInWeek = 7;
const hoursInDay = 24;
```

`const` works in exactly the same way as `let`, except that you can't give a `const` a new value. In the following example, the second line would throw an error:

```js
const daysInWeek = 7;
daysInWeek = 8;
```

Summary
-------

By now you should know a reasonable amount about JavaScript variables and how to create them. In the next article, we'll focus on numbers in more detail, looking at how to do basic math in JavaScript.

* 前頁
* Overview: First steps
* 次頁  (en-US)


初次接觸Javascript - 學習該如何開發 Web

初次接觸Javascript
==============

* 前頁
* Overview: First steps
* 次頁

目前你已經學會了一些 JavaScript 的理論，以及你能用它做些什麼。我們現在要透過一個完整的實際範例給你一個 JavaScript 基本功能的速成班。你將能一步一步地做出一個簡單的"猜數字"遊戲

|  |  |
| --- | --- |
| 先備知識: |
 基礎的電腦知識 , 有基礎的 HTML 跟 CSS 知識 ,還有知道 JavaScript
 是甚麼 .
  |
| 目標: |
 獲得第一次寫 JavaScript 的經驗 ,還有知道最基礎的 JavaScript
 程式該怎麼寫 .
  |

並不會要求你馬上就能仔細地了解所有程式碼 — 目前我們只是想介紹一些概觀，並向你介紹一些關於 JavaScript(以及其他程式語言)如何運作的知識。在接下來的章節你將會更仔細地瞭解這些功能！

**備註：** 你會在 JavaScript 看到許多跟其他程式語言一樣的特徵 — functions , loops 之類的 ，雖然程式語法看起來有差 ，但概念大部分都差不多 .

像程式工程師一樣思考
----------

寫程式中最困難的事情之一，不是你需要學習的語法，而是如何應用它來解決現實世界中的問題。 你需要開始像個程式設計師一樣思考 — 這通常與檢視程式目標的說明有關，並確定實現這些功能所需的程式碼，以及如何使它們協同工作。

這需要辛勤工作，程式語法經驗和練習 — 以及一點創造力。 你寫了越多程式碼，你就會越熟練。 我們不能保證你會在 5 分鐘內開發出「程式設計師的大腦」，但我們會給你很多機會在整個課程中練習「像程式設計師一樣思考」。

考慮到這一點，讓我們看一下我們將在本文中構建的範例，並審視將其分解為具體任務的大致流程。

範例 — 猜數字遊戲
----------

在本文中，我們將向你展示如何構建你可以在下面看到的簡單遊戲：
```

<!doctype html>
<html>
  <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;
 }

 .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="text" 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
 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;

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

 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 = "";
 }

 guessSubmit.addEventListener("click", checkGuess);

 function setGameOver() {
 guessField.disabled = true;
 guessSubmit.disabled = true;
 resetButton = document.createElement("button");
 resetButton.textContent = "Start new game";
 document.body.appendChild(resetButton);
 resetButton.addEventListener("click", resetGame);
 }

 function resetGame() {
 guessCount = 1;
 const resetParas = document.querySelectorAll(".resultParas p");
 for (let i = 0; i < resetParas.length; i++) {
 resetParas[i].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;
 }
 </script>
  </body>
</html>
```

好好玩一下遊戲再繼續吧 —— 在繼續前先與這個遊戲熟悉起來。

讓我們假設你的老闆給了你以下關於創建這個遊戲的簡介：

>
>
>  我要你幫我做一個很簡單的猜數字遊戲 .
>  玩家要在 10 回合內猜中一個 1 到 100 之間的隨機數字 ,
>  每回合結束時都要告訴玩家他們猜對還是猜錯 ,
>  然後要是他們猜錯 , 要告訴他們數字猜的太小還是太大 ,
>  這個遊戲會在玩家猜對 , 或是猜超過 10 次時結束 ,
>  且遊戲結束時 , 要提供一個選項讓玩家可以再玩一次 .
>
>
>
>

當看到上面的介紹後，我們可以做的第一件事就是開始拆解，盡可能的像個程式設計師，將它拆解為簡單可執行的任務：

1. 產生一個 1 到 100 間的隨機數字。
2. 從一開始，紀錄玩家目前回合數。
3. 提供玩家猜數字的方向(太大還是太小)。
4. 當玩家送出第一個猜測後，將猜測記錄下來，讓玩家可以看到他們之前的猜測。
5. 接著檢查數字是否猜中。
6. 如果數字猜對：
    1. 顯示恭喜訊息。
    2. 使玩家不能再輸入更多猜測(避免把遊戲玩壞)。
    3. 顯示控制鈕讓玩家可以重新開始遊戲。
7. 如果數字猜錯而且玩家有剩餘回合數：
    1. 告訴玩家他猜錯了。
    2. 讓玩家輸入其他的猜測
    3. 回合數增加 1。
8. 如果數字猜錯而且玩家沒有剩餘回合數：
    1. 告訴玩家遊戲結束。
    2. 使玩家不能再輸入更多猜測(避免把遊戲玩壞)。
    3. 顯示控制鈕讓玩家可以重新開始遊戲。
9. 當遊戲重新開始，確保遊戲邏輯和畫面(UI，使用這介面)全面重設，然後回到第一步。

現在，讓我們繼續向前，一路上我們檢視如何將這些步驟轉化為程式碼、建構出上面的範例與探索 JavaScript 的功能。

### 初步設定

在課程開始前，我們希望你可以複製一份number-guessing-game-start.html到自己的電腦中(see it live here)。用瀏覽器與文字編輯器將檔案打開時，你會看到簡單的標題、說明段落還有輸入猜測的表單，然而表單目前還不會做任何事情。

所有的程式碼都會放入置於 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;
```

這一區塊的程式碼設定我們的程式中用來儲存資料的變數。簡單的來說，「變數」是「值」的容器(值可以是數字、一串文字或是其他東西)。你可以用「關鍵字」(keyword) `let`(或是`var`)後面加上變數的名字來建立變數(在之後的文章你會看到兩者的差別)。利用關鍵字 `const` 建立常數，常數(Constant)是用來儲存你不會更改的值。我們用常數儲存使用者介面的參照，使用者介面中的文字可能會改變，但是參照所指的 HTML 元素的不會改變。

藉由等於符號(`=`)後面加上一個值，你可以指定變數或是常數的值。

在我們的範例中：

* 第一個變數 — `randomNumber` — 被指定成一個由數學運算的 1 到 100 間的隨機數字
* 接著的三個常數，分別儲存 HTML 中結果段落的參照，在後面的程式碼中，參照被用來插入一些數值 (注意他們都位於一個`<div>`元素裡，後者在之後我們重置遊戲時會被用來選取所有三個`<p>` (en-US)元素)：

```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="text" id="guessField" class="guessField" />
<input type="submit" value="Submit guess" class="guessSubmit" />
```

* 最後兩個變數，一個儲存回合數 1，另一個儲存指向重新開始按鈕的參照 (按鈕現在還不存在，之後會加上的)。

**備註：** 從下一篇文章開始，你會學到更多有關變數的事。

### 函式

下一步，將下面這段添加到之前寫的那段程式碼：

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

函式是一段可重複利用的程式碼塊。建立一個函式便可以反複運行並避免撰寫重複的程式碼。定義函式有很多方法，在此我們先專注在一種簡單的方式。這裡我們以關鍵字 `function` 、自訂的函式名、一對括號以及一對花括號（`{ }`）建立函式。花括號中的程式碼便是我們調用函式時所要實際執行的程式碼。

輸入函式名稱與括號便可以執行函式。

讓我們來試試吧。儲存你的程式碼並重新整理瀏覽器畫面。進入 開發者工具 JavaScript console (en-US), 並輸入下面這行：

```js
checkGuess();
```

當按下 `Return`/`Enter` 時，你會看到一個警告跳窗顯示「I am a placeholder」。我們已經在程式中定義好一個函式，只要我們調用這個函式，函式便會彈出一個警告視窗。

**備註：** 你會在後續的課程中學習到更多關於函式的事。

### 運算子

JavaScript 運算子可以讓我們執行比較、數學運算、連接字符串等。

儲存我們的程式碼並重整頁面，開啟 開發者工具 JavaScript console (en-US) 。接下來你可以試著輸入以下的範例 —— 輸入跟每個「範例」欄位中一樣的內容，每輸入一個就按下`Return` / `Enter`， 接著看看回傳的結果。

如果你不能快速打開瀏覽器開發工具， 你可以使用内嵌的應用程式中輸入以下範例：
```

<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>JavaScript console</title>
    <style>
 \* {
 box-sizing: border-box;
 }

 html {
 background-color: #0c323d;
 color: #809089;
 font-family: monospace;
 }

 body {
 max-width: 700px;
 }

 p {
 margin: 0;
 width: 1%;
 padding: 0 1%;
 font-size: 16px;
 line-height: 1.5;
 float: left;
 }

 .input p {
 margin-right: 1%;
 }

 .output p {
 width: 100%;
 }

 .input input {
 width: 96%;
 float: left;
 border: none;
 font-size: 16px;
 line-height: 1.5;
 font-family: monospace;
 padding: 0;
 background: #0c323d;
 color: #809089;
 }

 div {
 clear: both;
 }
 </style>
  </head>
  <body></body>

  <script>
 var geval = eval;

 function createInput() {
 var inputDiv = document.createElement("div");
 var inputPara = document.createElement("p");
 var inputForm = document.createElement("input");

 inputDiv.setAttribute("class", "input");
 inputPara.textContent = ">";
 inputDiv.appendChild(inputPara);
 inputDiv.appendChild(inputForm);
 document.body.appendChild(inputDiv);
 inputDiv.focus();

 if (document.querySelectorAll("div").length > 1) {
 inputForm.focus();
 }

 inputForm.addEventListener("change", executeCode);
 }

 function executeCode(e) {
 try {
 var result = geval(e.target.value);
 } catch (e) {
 var result = "error — " + e.message;
 }

 var outputDiv = document.createElement("div");
 var outputPara = document.createElement("p");

 outputDiv.setAttribute("class", "output");
 outputPara.textContent = "Result: " + result;
 outputDiv.appendChild(outputPara);
 document.body.appendChild(outputDiv);

 e.target.disabled = true;
 e.target.parentNode.style.opacity = "0.5";

 createInput();
 }

 createInput();
 </script>
</html>
```

首先讓我們看看以下的算數運算子：

| 運算子 | 名稱 | 範例 |
| --- | --- | --- |
| `+` | 加法 | `6 + 9` |
| `-` | 減法 | `20 - 15` |
| `*` | 乘法 | `3 * 7` |
| `/` | 除法 | `10 / 5` |

你也可以使用 `+` 來連接字串 (在程式設計中，這稱爲**連接**)。試著輸入以下幾行程式，每次一行：

```js
var name = "Bingo";
name;
var hello = " says hello!";
hello;
var greeting = name + hello;
greeting;
```

你也可以使用一些捷徑，這些被稱爲增量賦值運算子。如果你只是簡單想將兩個字串加在一起，你可以這樣做：

```js
name += " says hello!";
```

相當於

```js
name = name + " says hello!";
```

當我們進行真假值測試時 (例如下面)，我們可以使用比較運算子，如：

| 運算子 | 名稱 | 範例 |
| --- | --- | --- |
| `===` | 嚴格等於 (是否完全一樣?) | `5 === 2 + 4` |
| `!==` | 不等於 (是否不一樣?) | `'Chris' !== 'Ch' + 'ris'` |
| `<` | 小於 | `10 < 6` |
| `>` | 大於 | `10 > 20` |

### 條件

回到 `checkGuess()` 函式，我們希望的結果當然不只是彈出簡單訊息而已。我們更想要知道這個函式將如何檢查玩家的猜測是否準確，並回傳正確的結果。

所以現在將 `checkGuess()` 函式替換成下面這個版本：

```js
function checkGuess() {
  var userGuess = Number(guessField.value);
  if (guessCount === 1) {
    guesses.textContent = "Previous guesses: ";
  }
  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!!!";
    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();
}
```

哇，突然出現了很多程式碼！我們來完整地看一遍這些程式並介紹它們是如何運行的。

* 第一行（上面的第 2 行）宣告一個名為 `userGuess` 的變數，並將其值設置為在表單中文字輸入格內的當前值。我們還通過內建的 `Number()` 方法運行此值，以確保該值絕對是一個數字。
* 接下來，我們遇到第一個條件程式碼區塊（上面的第 3-5 行）。條件程式碼區塊允許你能基於某個條件是否為真來選擇性地執行程式碼。它看起來有點像函式，但其實並非如此。最簡單的條件區塊格式從 `if` 關鍵字開始，然後是一些括號，然後是一些花括號。在括號內包含一個比較測試。如果括號內的比較測試回傳 `true`，花括號內的程式碼則會被執行；否則不會執行，而會繼續執行接下來的程式碼。在這個情況下，我們的比較測試是檢查 `guessCount` 變數是否等於 `1`（即，這是否為玩家的第一次嘗試）：
```

guessCount === 1
```

如果是，我們將 `guesses` 段落的文字內容設為 「Previous guesses: 」。
* 第 6 行將當前 `userGuess` 的值附加到 `guesses` 段落的尾端，並加上一個空格，讓顯示的每次猜測紀錄之間都有一個空格。
* 下一個條件區塊（上面第 8-24 行）做了一些檢查：
    + 第一個 `if(){ }` 檢查用戶的猜測是否等於`我們`JavaScript 頂部設置的 `randomNumber`。如果是，代表玩家猜對了，贏了遊戲，所以我們向玩家顯示一個漂亮的綠色祝賀訊息，清除 `lowOrHigh` 段落的內容，並運行一個叫做 `setGameOver()` 的函式，這個函式我們稍後再討論。
    + 現在我們使用 `else if(){ }` 區塊將另一個測試連接到上一個測試的後面。這個測試檢查本次猜測是否為玩家的最後一次猜測機會。如果是，則執行與上一個條件區塊相同的操作，只是這次是遊戲結束訊息，漂亮的綠色祝賀訊息。
    + 連接到此條件區塊後面的最後一個區塊（`else { }`）只會在其他兩個測試都沒有返回 `true` 時執行（即，玩家沒有猜對，但他們還有更多的猜測機會）。在這種情況下，我們告訴他們猜錯了，然後我們執行另一個條件測試以檢查猜測是高於還是低於正確答案 `randomNumber`，並顯示進一步的訊息來告訴他們要猜得更高或更低。
* 函式中的最後三行（上面的第 26-28 行）讓玩家可以提交下一個猜測。我們將`guessCount`變數加 1，來增加玩家已使用的猜測次數（`++`是遞增操作 - 遞增 1），並將表單中文字輸入格內的文字清空並給予其焦點（顯示文字輸入游標），準備好讓玩家輸入下一個猜測。
### 事件

現在我們有了一個很好的 `checkGuess()` 函式，但它並不會做任何事情，因為我們還沒有呼叫它。我們想在按下 「Submit guess」 按鈕時呼叫它，為此，我們需要使用**事件**。事件是在瀏覽器中發生的操作，例如單擊按鈕，加載頁面或播放影片，以讓我們可以在這些操作發生時執行程式碼。偵聽事件發生的構造稱為**事件偵聽器**，偵聽事件而觸發執行的程式碼稱為**事件處理器**。

在 `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.appendChild(resetButton);
  resetButton.addEventListener("click", resetGame);
}
```

* 函數中的前兩行透過將表單的文字輸入和按鈕的 `disabled` 屬性設為 `true` 來將兩者無效化。這很重要，因為如果沒有這麼做，使用者可能會在遊戲結束後繼續送出更多猜測值，這也許會把東西弄得一團糟。
* 接下來的三行會生成一個新的 `<button>` (en-US) 元素，將它的文字標籤設為「Start new game」後，添加到我們的 HTML 的最尾端。
* 最後一行則會在我們的新按鈕上設置一個事件偵聽器，來讓使用者按下按鈕時執行一個叫 `resetGame()` 的函數。

現在讓我們來定義 `resetGame()`！再次將下面這些程式碼加進你的 JavaScript 的最下方。

```js
function resetGame() {
  guessCount = 1;

  var resetParas = document.querySelectorAll(".resultParas p");
  for (var i = 0; i < resetParas.length; i++) {
    resetParas[i].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。
* 清除所有訊息段落。
* 將重置按鈕移除。
* 將表單元素有效化，清空文字輸入並給予其焦點，準備好讓玩家能夠進行新一輪的猜測。
* `將lastResult` 段落的背景顏色設回白色。
* 生成一個新的隨機數值，才不會讓玩家又猜一次重複的答案！

**現在，你應該有了一個完整且能正常執行的簡單遊戲了 — 恭喜你啦！**

接下來這篇文章的工作只剩下來談談其他幾個很重要的程式功能，你應該已經看過了，只是還沒察覺罷了。

### 迴圈

上面的程式碼中，一個我們需要仔細看看的部份是 for 迴圈。迴圈在程式設計中是一個非常重要的內容，可以讓你在滿足條件前反覆執行同一段程式碼。

開始吧，打開你的 開發者工具 JavaScript console (en-US)，然後輸入下面這行：

```js
for (var i = 1; i < 21; i++) {
  console.log(i);
}
```

看見了嗎？在你的主控台內印出了數字 1 到 20。這就是迴圈的效果。一個 for 迴圈需要三個參數：

1. \*\*起始動作：\*\*這個例子中我們從 1 開始累加，這個起始數值可以是任何你想要的值。你也可以不要使用 `i` 這個變數名稱，但習慣上我們會使用 `i` ，因為它簡單好記。
2. \*\*離開條件：\*\*這裡我們指定了 `i < 21` — 這個迴圈會一直執行直到 `i` 不再小於 21。當 `i` 達到 21，這個迴圈就會停止執行。
3. \*\*增加動作：\*\*我們指定了 `i++`，「將 `i` 的值加 1」。這個迴圈會對每個 `i` 的值執行一次，直到 `i` 達到 21（如上一條所述）。這個例子中，我們簡單的透過 `console.log()` (en-US) 將每次迴圈中 `i` 的值輸出到主控台中。

現在我們來看看在猜謎遊戲中的迴圈 — 這可以在 `resetGame()` 函式中找到：

```js
var resetParas = document.querySelectorAll(".resultParas p");
for (var i = 0; i < resetParas.length; i++) {
  resetParas[i].textContent = "";
}
```

這段程式碼透過呼叫 `querySelectorAll()` (en-US) 創建一個變數並存著一個在 `<div class="resultParas">` 中的所有段落清單，然後使用迴圈來遍歷每個段落元素，並移除其內容。

### 稍微討論一下物件

在開始討論這個段落的話題前，先來做點小小修改。在你的 JavaScript 接近頂部的 `var resetButton` 下一行加上：

```js
guessField.focus();
```

，然後存檔。

這一行呼叫了 `focus()` (en-US) 方法來在頁面讀取時，將輸入游標自動放進 `<input>` (en-US) 文字欄裡面，這意味著使用者在開啟頁面後就可以直接使用鍵盤來在文字欄內輸入文字，而不用先點選文字欄。這只是個小修改，可是大大的提升了使用體驗，也給了使用者清楚的提示 — 提示他們要做些什麼來遊玩這個遊戲。

讓我們來分析一下究竟發生了什麼事。在 JavaScript 中，所有東西都是一個**物件**。物件是一個集合，由許多相關的功能打包成一體。你可以創建一個你自己的物件，不過這比較進階，我們現在並不會涵蓋這個內容，這些會在課程的後期提到。現在，我們只會簡要的提到一些你的瀏覽器內建的物件，他們能夠讓你做到許多有用的事。

在這個例子中，我們首先創建了一個 `guessField` 變數，儲存著一個指向 HTML 表單中文字輸入欄的參照 — 這可以在我們定義變數的區塊中找到：

```js
var guessField = document.querySelector(".guessField");
```

我們使用了 `querySelector()` 來取得這個參照，前者是 `document` (en-US) 物件的方法。`querySelector()` 接受一個參數 — 一個 CSS 選擇器， 會回傳你想要的元素參照。

因為現在 `guessField` 中存著一個指向 `<input>` (en-US) 元素的參照，它現在可以存取這個元素的屬性（基本上就是存在物件中的變數，其中有一些可能會是常數）和方法（基本上就是存在物件中的函式）了。文字輸入欄的其中一個方法便是 `focus()`，我們便可以透過呼叫這個方法來給予其焦點：

```js
guessField.focus();
```

 沒有存著表單元素參照的變數就不會有 `focus()` 方法能使用。
 例如存著一個 `<p>` (en-US) 元素的 `guesses` 和存著一個數值的 `guessCount`。

### 來玩玩瀏覽器物件

讓我們來稍微玩一點瀏覽器內建的物件吧。

1. 首先在瀏覽器中開啟你的程式。
2. 接下來，打開你的開發者工具 JavaScript console (en-US)。
3. 輸入 `guessField`，可以看到主控台顯示這個變數儲存著一個 `<input>` (en-US) 元素。你還可以發現主控台會自動幫你完成已存在的物件名稱！
4. 接下來輸入：

```js
guessField.value = "Hello";
```

`value` 屬性儲存著現在文字輸入欄內的內容參照。現在按下
 `Enter`
 ，看看文字欄內的內容是不是變了？
5. 試著輸入 `guesses` 然後按下
 `Enter`
 ，主控台會顯示這個變數儲存著一個 `<p>` (en-US) 元素。
6. 現在輸入：

```js
guesses.value;
```

瀏覽器會回傳 `undefined`，因為 `value` 不存在在段落元素裡面。
7. 要更改段落元素中的文字，你需要的是 `textContent` (en-US) 屬性。試試這個：

```js
guesses.textContent = "Where is my paragraph?";
```

8. 現在來做些好玩的事。一行一行輸入並
 `Enter`
 ：

```js
guesses.style.backgroundColor = "yellow";
guesses.style.fontSize = "200%";
guesses.style.padding = "10px";
guesses.style.boxShadow = "3px 3px 6px black";
```

每個在頁面上的元素都有一個 `style` 屬性，其本身又是另一個物件，包含著許多該元素的行內 CSS 屬性。這讓我們能透過 JavaScript 來動態的為元素設置 CSS 屬性。
差不多就到這了
-------

這就是我們的範例 — 你順利地來到結尾了，做得不錯！試試你的最終成品，或試試我們的版本。如果你仍然有困難沒有解決，再看看我們的原始碼。

* 前頁
* Overview: First steps
* 次頁


傻故事產生器 - 學習該如何開發 Web

傻故事產生器
======

* 前頁
* Overview: First steps

在本次評估中，你被賦予的任務內容將與本單元學習到的知識息息相關，並將其應用於創建一個能隨機生成傻故事的有趣應用程式。 祝玩的開心！

|  |  |
| --- | --- |
| 事先具備: | 進行此測驗之前你應先完成此區塊的所有內容 |
| 目標: |
 測試對於JavaScript基礎的理解程度, 例如 變數, 數字, 運算子, 字串
 以及陣列.
  |

前置作業
----

在開始本測驗前，你應該:

* 開啟右方網頁： grab the HTML file ，將 HTML 範本儲存到你的本地電腦的新資料夾中，並命名為`index.html` 。範本也包含相應的 CSS 檔案。
* 開啟右方網頁： page containing the raw text ，並保持網頁開啟在瀏覽器的另一分頁上，以便稍後使用。

**備註：** 除了將檔案下載到自己的電腦中，你也能使用線上編輯程式的網頁，像是：JSBin 或者 Glitch 來完成評估測驗。你可以將 HTML, CSS 以及 JavaScript 貼到前述的線上編輯器中。如果你使用的線上編輯器沒有獨立給 JavaScript 的編輯區，你也能透過`<script>`直接將 JS 語法放到 HTML 檔案中。

任務簡介
----

透過前述網頁你已經得到初版 HTML/CSS 與一些 JavaScript 字串、函數；你需要再寫一些必要的 JavaScript 語法來將這些檔案轉變為可運作的程式，任務如下：

* 當 "Generate random story" (產生隨機故事)的按鈕被點擊，請產出一則傻故事
* 若在點擊產生故事按鈕前將自定義名字輸入於輸入框"Enter custom name" (譯：輸入自定義名字），則將故事中預設的名字 "Bob" 代換為自定義名字
* 若在點擊產生故事按鈕前，也點擊 UK 的單選按鈕，則將預設的美制重量、溫度單位轉換為英制單位
* 每點擊一次產生故事的按鈕，即產生新一則隨機傻故事（不限次數）

以下截圖為完成品的範例：

![](/zh-TW/docs/Learn/JavaScript/First_steps/Silly_story_generator/screen_shot_2018-09-19_at_10.01.38_am.png)

點擊右方連結可以參考與測試完成品： have a look at the finished example （請不要偷看原始碼喔！）

任務開始
----

以下清楚地描述了完成任務需要哪些動作。

基本設定：

1. 在有`index.html`的資料夾中建立一個新檔案稱之為 `main.js`
2. 請在`index.html`中引用第一點建立的外部 JavaScript 檔案，引用方法是在`</body>` tag 前插入一組 `<script>`元素 ，並在 opening tag 上加入`src=" main.js"`

初始化變數與函數：

1. 在原始文件檔中(raw text file)，請複製標題 1. COMPLETE VARIABLE AND FUNCTION DEFINITIONS" 以下到第 2 點前的所有程式碼，並貼到`main.js`中。這給你三個變數來標記：文字輸入框 "Enter custom name" (輸入自定義名字) ，變數為 (`customName`) 與按鈕 "Generate random story"(產生隨機故事) ，變數為 (`randomize`), 以及 HTML 中接近 body 底部的 `<p>` (en-US) 元素，故事將會被複製進第三個變數(`story`)中。此外你還會得到一個函數稱為： `randomValueFromArray()` ，從命名中可以得知這是一個陣列，它會隨機提供一則儲存其中的故事。
2. 接著讓我們查看原始文件檔中(raw text file)的第 2 點： "2. RAW TEXT STRINGS"。 其包含的這些字串在程式運行時會被放進來，請幫忙在`main.js`中將這些字串分別存進對應的變數裡：
    1. 將第一行超級長的字串存進變數 `storyText`中。
    2. 將第一組三個字串存進一陣列，並命名為`insertX`。
    3. 將第二組三個字串存進一陣列，並命名為`insertY`。
    4. 將第三組三個字串存進一陣列，並命名為`insertZ`。

放置事件監聽器與未完善的函數：

1. 再度回到原始文件檔中(raw text file)
2. 複製第 3 標題，"3. EVENT LISTENER AND PARTIAL FUNCTION DEFINITION" 以下的內容，並貼到 `main.js` 檔案中的最下方，這包含：
    * 給變數`randomize`增加一個點擊事件監聽器（clickevent listener），所以當產生故事的按鈕被點擊，`result()` 函數會運行 。
    * 增加一個部分完成的函數 `result()`，完成測驗你需要完善這個函數。

完善 `result()` 函數：

1. 創造一個新變數稱為：`newStory`，讓這個變數的值等於`storyText`；我們需要這個變數以便每次產生故事按鈕被點擊時，函數都能再次運作並產生新故事，如果我們只在`storyText`之上做改變，我們只能產生一次新故事。
2. 額外增加三個變數：`xItem`、`yItem` 與 `zItem`，並使這三個變數等於函數`randomValueFromArray()`中三個陣列的結果(每次會從各陣列中隨機挑出一項)。舉例，你能透過寫`randomValueFromArray(insertX)`來從`insertX`得到任一隨機字串。
3. 接著我們需要將`newStory`中三個 placeholders 字串 `:insertx:`、`:inserty:`跟 `:insertz:`換成`xItem`、`yItem`、 `zItem`。有些字串方法在這裡特別有用，請讓字串方法的返回值等於 `newStory` ，所以之後每次 `newStory` 被呼叫時，is made equal to itself, but with substitutions made. So each time the button is pressed, these placeholders are each replaced with a random silly string. As a further hint, the method in question only replaces the first instance of the substring it finds, so you might need to make one of the calls twice.
4. Inside the first `if` block, add another string replacement method call to replace the name 'Bob' found in the `newStory` string with the `name` variable. In this block we are saying "If a value has been entered into the `customName` text input, replace Bob in the story with that custom name."
5. Inside the second `if` block, we are checking to see if the `uk` radio button has been selected. If so, we want to convert the weight and temperature values in the story from pounds and Fahrenheit into stones and centigrade. What you need to do is as follows:
    1. Look up the formulae for converting pounds to stone, and Fahrenheit to centigrade.
    2. Inside the line that defines the `weight` variable, replace 300 with a calculation that converts 300 pounds into stones. Concatenate `' stone'` onto the end of the result of the overall `Math.round()` call.
    3. Inside the line that defines the `temperature` variable, replace 94 with a calculation that converts 94 Fahrenheit into centigrade. Concatenate `' centigrade'` onto the end of the result of the overall `Math.round()` call.
    4. Just under the two variable definitions, add two more string replacement lines that replace '94 farenheit' with the contents of the `temperature` variable, and '300 pounds' with the contents of the `weight` variable.
6. Finally, in the second-to-last line of the function, make the `textContent` property of the `story` variable (which references the paragraph) equal to `newStory`.
Hints and tips
--------------

* You don't need to edit the HTML in any way, except to apply the JavaScript to your HTML.
* If you are unsure whether the JavaScript is applied to your HTML properly, try removing everything else from the JavaScript file temporarily, adding in a simple bit of JavaScript that you know will create an obvious effect, then saving and refreshing. The following for example turns the background of the `<html>` (en-US) element red — so the entire browser window should go red if the JavaScript is applied properly:

```js
document.querySelector("html").style.backgroundColor = "red";
```

* Math.round() is a built-in JavaScript method that simply rounds the result of a calculation to the nearest whole number.
* There are three instances of strings that need to be replaced. You may repeat the `replace()` method multiple times, or you can use regular expressions. For instance, `var text = 'I am the biggest lover, I love my love'; text.replace(/love/g,'like');` will replace all instances of 'love' to 'like'. Remember, Strings are immutable!
測驗一下
----

如果你將這個測驗視為正規課程的一部分，建議將成果提供你的老師或指導者以利幫助你達到最好的學習效益。如果你是自學者，你可以輕鬆的透過右方網頁 discussion thread for this exercise 得到建議，或者在Mozilla IRC上的 #mdn IRC 頻道。提醒你：第一次嘗試這個測驗時，作弊可不會得到任何收穫喔！

* 前頁
* Overview: First steps


Arrays - 學習該如何開發 Web

Arrays
======

* 前頁
* Overview: First steps
* 次頁

在本單元的最後一篇文章中，我們將介紹陣列——一種在單個變數名下儲存資料項列表的簡潔方法。在這裡，我們看看為什麼這很有用，然後探討如何建立陣列，檢索、增加和刪除儲存在陣列中的項目等等。

|  |  |
| --- | --- |
| 先備知識： | 基本計算機知識、基本理解 HTML 與 CSS、知道 JavaScript 是什麼。 |
| 目標： | 理解何謂陣列並在 JavaScript 操作之。 |

什麼是陣列？
------

陣列通常描述為「像列表的物件」：也就是一個列表物件，裡面含有幾個數值。陣列物件能放在變數裡面，處理方式也與資料型別大致相同。不過主要差異為，陣列可以獨立存取、並高效處理裡面的數值：像是利用迴圈，對每個數值作相同處理。例如我們的陣列是一組有項目和價格的產品、我們可以用迴圈把單價印在發票上、最後在發票底下印出合計。

不用陣列的話，就會需要註冊、並單獨呼叫很多獨立變數。這樣會花更多時間寫程式、效率更低、還更容易寫錯。只有十個的話還好解決，但如果有一百個，甚至一千個呢？我們會在接下來闡述之。

與前幾篇文章一樣，讓我們在 JavaScript 控制台中輸入一些示例，來了解陣列的基礎知識吧。

### 建立陣列

陣列用方括弧包起來，每個單位會用逗號分隔起來。

1. 來作一個購物清單的陣列吧：我們會做類似下面的事情。在主控台中輸入以下程式：

```js
var shopping = ["bread", "milk", "cheese", "hummus", "noodles"];
shopping;
```

2. 在此，陣列的每個單位都是字串。但請記住，陣列可以儲存任何單位：字串、數字、物件、另一個變數、甚至是另一個陣列。也可以混合單位的型別：它們不一定都要是數字或字串。來試試這個：

```js
var sequence = [1, 1, 2, 3, 5, 8, 13];
var random = ["tree", 795, [0, 1, 2]];
```

3. 看下去之前，試著自己作幾個陣列。
### 存取並修改陣列的單位

你可以使用括號標記法存取個別單位，同時也可以存取字串中的字母。

1. 在主控台輸入以下程式：

```js
shopping[0];
// returns "bread"
```

2. 也可以透過賦予陣列新數值修改該單位。試試這個：

```js
shopping[0] = "tahini";
shopping;
// shopping 回傳 [ "tahini", "milk", "cheese", "hummus", "noodles" ]
```

**備註：** 前面有說過，但還是提醒下：電腦從 0 開始數！
3. 請注意，陣列裡面的陣列稱為多維陣列（multidimensional array）。你可以撰寫兩組方括弧，來存取陣列裡面的陣列單位。例如說，存取前述 `random` 變數內的陣列單位就可以這麼寫：

```js
random[2][2];
```

4. 看下去之前，試著進一步使用並修改陣列。
### 找出陣列長度

找出陣列長度（意即有幾個單位在陣列內）的方法，跟找出字串長度（含有幾個字元）的方式一樣——都是使用 `length` 屬性。試試下方程式行：

```js
shopping.length;
// should return 5
```

這還有其他用途，但最常見的用法是讓迴圈一直循環直到所有的單元都走過一次。 舉個例子：

```js
var sequence = [1, 1, 2, 3, 5, 8, 13];
for (var i = 0; i < sequence.length; i++) {
  console.log(sequence[i]);
}
```

在後續的章節，你會學到更多關於迴圈的部分；簡而言之，上述程式碼的意思是：

1. 從陣列中索引為 0 的單元開始循環。
2. 當索引值等於陣列的長度時，停止循環。這個方法對任何長度的陣列都可行，但在這個例子中，迴圈會當索引等於 7 時停止循環（這樣很好，因為最後一個單元——我們希望有包含到的——是 6）。
3. 我們在瀏覽器 console 中用 `console.log()` (en-US) 將每個單元列印出來。
好用的陣列方法
-------

在這個小節中，我們會介紹一些相當有用、有關陣列的方法。例如將字串拆分為陣列，反之亦然，以及增加新的單位到陣列中。

### 在字串與陣列之間轉換

通常你會看到一組含有原始資料的長字串，而你可能會希望將有用的單元拆分、組成更好用的形式，對他進行操作。為了達成這個目的，我們可以使用 `split()` (en-US) 方法。它最簡單的形式是只使用一個參數，你想分離的字串位置的字元（分隔符），而後它會返回陣列中在分隔符之間的子字串。

**備註：** 好的，在技術上它屬於字串的方法，而非陣列的方法。但因為它可以很順利地對陣列進行操作，因此我們把它列在這邊。

1. 來試試這個，看它如何運作。首先，建立一個字串在你的 console:

```js
var myData = "Manchester,London,Liverpool,Birmingham,Leeds,Carlisle";
```

2. 現在我們用逗點來分隔字串：

```js
var myArray = myData.split(",");
myArray;
```

3. 最後，試著找出你新的陣列的長度，並且從中取出一些單元：

```js
myArray.length;
myArray[0]; // the first item in the array
myArray[1]; // the second item in the array
myArray[myArray.length - 1]; // the last item in the array
```

4. 相對地，你也可以用 `join()` 方法。試試下面這段：

```js
var myNewString = myArray.join(",");
myNewString;
```

5. 另一個將陣列轉換為字串的方法是用 `toString()` 。`toString()` 因為不需要參數而比 `join()` 更簡潔，但因此也更多限制。使用 `join()` 你可以使用特定的分隔符（試著使用其他不同的字元來執行步驟 4）。

```js
var dogNames = ["Rocket", "Flash", "Bella", "Slugger"];
dogNames.toString(); //Rocket,Flash,Bella,Slugger
```

### 新增與移除陣列單位

我們還沒談到增加與移除陣列的單位，現在來看看吧！我們使用上一個小節中的 `myArray` 陣列。如果你沒跟隨到上一個小節，那就先在你的 console 建立下面這個陣列：

```js
var myArray = [
  "Manchester",
  "London",
  "Liverpool",
  "Birmingham",
  "Leeds",
  "Carlisle",
];
```

首先，我們可以分別使用 `push()` 和 `pop()` 來增加或移除一個在陣列最末端的單元 。

1. Let's use `push()` first — note that you need to include one or more items that you want to add to the end of your array. Try this:

```js
myArray.push("Cardiff");
myArray;
myArray.push("Bradford", "Brighton");
myArray;
```

2. The new length of the array is returned when the method call completes. If you wanted to store the new array length in a variable, you could do something like this:

```js
var newLength = myArray.push("Bristol");
myArray;
newLength;
```

3. Removing the last item from the array is as simple as running `pop()` on it. Try this:

```js
myArray.pop();
```

4. The item that was removed is returned when the method call completes. To save that item in a new variable, you could do this:

```js
var removedItem = myArray.pop();
myArray;
removedItem;
```

`unshift()` and `shift()` work in exactly the same way as `push()` and `pop()`, respectively, except that they work on the beginning of the array, not the end.

1. First `unshift()` — try the following commands:

```js
myArray.unshift("Edinburgh");
myArray;
```

2. Now `shift()`; try these!

```js
var removedItem = myArray.shift();
myArray;
removedItem;
```

Active learning: Printing those products
----------------------------------------

Let's return to the example we described earlier — printing out product names and prices on an invoice, then totaling the prices and printing them at the bottom. In the editable example below there are comments containing numbers — each of these marks a place where you have to add something to the code. They are as follows:

1. Below the `// number 1` comment are a number of strings, each one containing a product name and price separated by a colon. We'd like you to turn this into an array and store it in an array called `products`.
2. On the same line as the `// number 2` comment is the beginning of a for loop. In this line we currently have `i <= 0`, which is a conditional test that causes the for loop to stop immediately, because it is saying "stop when `i` is no longer less than or equal to 0", and `i` starts at 0. We'd like you to replace this with a conditional test that stops the loop when `i` is no longer less than the `products` array's length.
3. Just below the `// number 3` comment we want you to write a line of code that splits the current array item (`name:price`) into two separate items, one containing just the name and one containing just the price. If you are not sure how to do this, consult the Useful string methods article for some help, or even better, look at the Converting between strings and arrays section of this article.
4. As part of the above line of code, you'll also want to convert the price from a string to a number. If you can't remember how to do this, check out the first strings article.
5. There is a variable called `total` that is created and given a value of 0 at the top of the code. Inside the loop (below `// number 4`) we want you to add a line that adds the current item price to that total in each iteration of the loop, so that at the end of the code the correct total is printed onto the invoice. You might need an assignment operator to do this.
6. We want you to change the line just below `// number 5` so that the `itemText` variable is made equal to "current item name — $current item price", for example "Shoes — $23.99" in each case, so the correct information for each item is printed on the invoice. This is just simple string concatenation, which should be familiar to you.
```

<h2>Live output</h2>

<div class="output" style="min-height: 150px;">
  <ul></ul>

  <p></p>
</div>

<h2>Editable code</h2>

<p class="a11y-label">
  Press Esc to move focus away from the code area (Tab inserts a tab character).
</p>

<textarea id="code" class="playable-code" style="height: 410px;width: 95%">
var list = document.querySelector('.output ul');
var totalBox = document.querySelector('.output p');
var total = 0;
list.innerHTML = '';
totalBox.textContent = '';
// number 1
                'Underpants:6.99'
                'Socks:5.99'
                'T-shirt:14.99'
                'Trousers:31.99'
                'Shoes:23.99';

for (var i = 0; i <= 0; i++) { // number 2
  // number 3

  // number 4

  // number 5
  itemText = 0;

  var listItem = document.createElement('li');
  listItem.textContent = itemText;
  list.appendChild(listItem);
}

totalBox.textContent = 'Total: $' + total.toFixed(2);
</textarea>

<div class="playable-buttons">
  <input id="reset" type="button" value="Reset" />
  <input id="solution" type="button" value="Show solution" />
</div>
```

```
var textarea = document.getElementById("code");
var reset = document.getElementById("reset");
var solution = document.getElementById("solution");
var code = textarea.value;
var userEntry = textarea.value;

function updateCode() {
  eval(textarea.value);
}

reset.addEventListener("click", function () {
  textarea.value = code;
  userEntry = textarea.value;
  solutionEntry = jsSolution;
  solution.value = "Show solution";
  updateCode();
});

solution.addEventListener("click", function () {
  if (solution.value === "Show solution") {
    textarea.value = solutionEntry;
    solution.value = "Hide solution";
  } else {
    textarea.value = userEntry;
    solution.value = "Show solution";
  }
  updateCode();
});

var jsSolution =
  "var list = document.querySelector('.output ul');\nvar totalBox = document.querySelector('.output p');\nvar total = 0;\nlist.innerHTML = '';\ntotalBox.textContent = '';\n\nvar products = ['Underpants:6.99',\n 'Socks:5.99',\n 'T-shirt:14.99',\n 'Trousers:31.99',\n 'Shoes:23.99'];\n\nfor(var i = 0; i < products.length; i++) {\n var subArray = products[i].split(':');\n var name = subArray[0];\n var price = Number(subArray[1]);\n total += price;\n itemText = name + ' — $' + price;\n\n var listItem = document.createElement('li');\n listItem.textContent = itemText;\n list.appendChild(listItem);\n}\n\ntotalBox.textContent = 'Total: $' + total.toFixed(2);";
var solutionEntry = jsSolution;

textarea.addEventListener("input", updateCode);
window.addEventListener("load", updateCode);

// stop tab key tabbing out of textarea and
// make it write a tab at the caret position instead

textarea.onkeydown = function (e) {
  if (e.keyCode === 9) {
    e.preventDefault();
    insertAtCaret("\t");
  }

  if (e.keyCode === 27) {
    textarea.blur();
  }
};

function insertAtCaret(text) {
  var scrollPos = textarea.scrollTop;
  var caretPos = textarea.selectionStart;

  var front = textarea.value.substring(0, caretPos);
  var back = textarea.value.substring(
    textarea.selectionEnd,
    textarea.value.length,
  );
  textarea.value = front + text + back;
  caretPos = caretPos + text.length;
  textarea.selectionStart = caretPos;
  textarea.selectionEnd = caretPos;
  textarea.focus();
  textarea.scrollTop = scrollPos;
}

// Update the saved userCode every time the user updates the text area code

textarea.onkeyup = function () {
  // We only want to save the state when the user code is being shown,
  // not the solution, so that solution is not saved over the user code
  if (solution.value === "Show solution") {
    userEntry = textarea.value;
  } else {
    solutionEntry = textarea.value;
  }

  updateCode();
};
```

```
html {
  font-family: sans-serif;
}

h2 {
  font-size: 16px;
}

.a11y-label {
  margin: 0;
  text-align: right;
  font-size: 0.7rem;
  width: 98%;
}

body {
  margin: 10px;
  background-color: #f5f9fa;
}
```

Active learning: Top 5 searches
-------------------------------

A good use for array methods like `push()` and `pop()` is when you are maintaining a record of currently active items in a web app. In an animated scene for example, you might have an array of objects representing the background graphics currently displayed, and you might only want 50 displayed at once, for performance or clutter reasons. As new objects are created and added to the array, older ones can be deleted from the array to maintain the desired number.

In this example we're going to show a much simpler use — here we're giving you a fake search site, with a search box. The idea is that when terms are entered in the search box, the top 5 previous search terms are displayed in the list. When the number of terms goes over 5, the last term starts being deleted each time a new term is added to the top, so the 5 previous terms are always displayed.

**備註：** In a real search app, you'd probably be able to click the previous search terms to return to previous searches, and it would display actual search results! We are just keeping it simple for now.

To complete the app, we need you to:

1. Add a line below the `// number 1` comment that adds the current value entered into the search input to the start of the array. This can be retrieved using `searchInput.value`.
2. Add a line below the `// number 2` comment that removes the value currently at the end of the array.
```

<h2>Live output</h2>
<div class="output" style="min-height: 150px;">
  <input type="text" /><button>Search</button>

  <ul></ul>
</div>

<h2>Editable code</h2>

<p class="a11y-label">
  Press Esc to move focus away from the code area (Tab inserts a tab character).
</p>

<textarea id="code" class="playable-code" style="height: 370px; width: 95%">
var list = document.querySelector('.output ul');
var searchInput = document.querySelector('.output input');
var searchBtn = document.querySelector('.output button');

list.innerHTML = '';

var myHistory = [];

searchBtn.onclick = function() {
  // we will only allow a term to be entered if the search input isn't empty
  if (searchInput.value !== '') {
    // number 1

    // empty the list so that we don't display duplicate entries
    // the display is regenerated every time a search term is entered.
    list.innerHTML = '';

    // loop through the array, and display all the search terms in the list
    for (var i = 0; i < myHistory.length; i++) {
      itemText = myHistory[i];
      var listItem = document.createElement('li');
      listItem.textContent = itemText;
      list.appendChild(listItem);
    }

    // If the array length is 5 or more, remove the oldest search term
    if (myHistory.length >= 5) {
      // number 2

    }

    // empty the search input and focus it, ready for the next term to be entered
    searchInput.value = '';
    searchInput.focus();
  }
}
</textarea>

<div class="playable-buttons">
  <input id="reset" type="button" value="Reset" />
  <input id="solution" type="button" value="Show solution" />
</div>
```

```
html {
  font-family: sans-serif;
}

h2 {
  font-size: 16px;
}

.a11y-label {
  margin: 0;
  text-align: right;
  font-size: 0.7rem;
  width: 98%;
}

body {
  margin: 10px;
  background: #f5f9fa;
}
```

```
var textarea = document.getElementById("code");
var reset = document.getElementById("reset");
var solution = document.getElementById("solution");
var code = textarea.value;
var userEntry = textarea.value;

function updateCode() {
  eval(textarea.value);
}

reset.addEventListener("click", function () {
  textarea.value = code;
  userEntry = textarea.value;
  solutionEntry = jsSolution;
  solution.value = "Show solution";
  updateCode();
});

solution.addEventListener("click", function () {
  if (solution.value === "Show solution") {
    textarea.value = solutionEntry;
    solution.value = "Hide solution";
  } else {
    textarea.value = userEntry;
    solution.value = "Show solution";
  }
  updateCode();
});

var jsSolution =
  "var list = document.querySelector('.output ul');\nvar searchInput = document.querySelector('.output input');\nvar searchBtn = document.querySelector('.output button');\n\nlist.innerHTML = '';\n\nvar myHistory= [];\n\nsearchBtn.onclick = function() {\n if(searchInput.value !== '') {\n myHistory.unshift(searchInput.value);\n\n list.innerHTML = '';\n\n for(var i = 0; i < myHistory.length; i++) {\n itemText = myHistory[i];\n var listItem = document.createElement('li');\n listItem.textContent = itemText;\n list.appendChild(listItem);\n }\n\n if(myHistory.length >= 5) {\n myHistory.pop();\n }\n\n searchInput.value = '';\n searchInput.focus();\n }\n}";
var solutionEntry = jsSolution;

textarea.addEventListener("input", updateCode);
window.addEventListener("load", updateCode);

// stop tab key tabbing out of textarea and
// make it write a tab at the caret position instead

textarea.onkeydown = function (e) {
  if (e.keyCode === 9) {
    e.preventDefault();
    insertAtCaret("\t");
  }

  if (e.keyCode === 27) {
    textarea.blur();
  }
};

function insertAtCaret(text) {
  var scrollPos = textarea.scrollTop;
  var caretPos = textarea.selectionStart;

  var front = textarea.value.substring(0, caretPos);
  var back = textarea.value.substring(
    textarea.selectionEnd,
    textarea.value.length,
  );
  textarea.value = front + text + back;
  caretPos = caretPos + text.length;
  textarea.selectionStart = caretPos;
  textarea.selectionEnd = caretPos;
  textarea.focus();
  textarea.scrollTop = scrollPos;
}

// Update the saved userCode every time the user updates the text area code

textarea.onkeyup = function () {
  // We only want to save the state when the user code is being shown,
  // not the solution, so that solution is not saved over the user code
  if (solution.value === "Show solution") {
    userEntry = textarea.value;
  } else {
    solutionEntry = textarea.value;
  }

  updateCode();
};
```

Conclusion
----------

After reading through this article, we are sure you will agree that arrays seem pretty darn useful; you'll see them crop up everywhere in JavaScript, often in association with loops in order to do the same thing to every item in an array. We'll be teaching you all the useful basics there are to know about loops in the next module, but for now you should give yourself a clap and take a well-deserved break; you've worked through all the articles in this module!

The only thing left to do is work through this module's assessment, which will test your understanding of the articles that came before it.

See also
--------

* Indexed collections (en-US) — an advanced level guide to arrays and their cousins, typed arrays.
* `Array` — the `Array` object reference page — for a detailed reference guide to the features discussed in this page, and many more.
* 前頁
* Overview: First steps
* 次頁


處理文字 - JavaScript中的字串 - 學習該如何開發 Web

處理文字 - JavaScript中的字串
=====================

* 前頁
* Overview: First steps
* 次頁

接下來我們將把注意力轉向字串——這就是程式設計中調用的文字片段。在本文中，我們將介紹在學習 JavaScript 時你應該了解所有有關字串的常見事項，例如建立字串，跳脫字串中的引號以及將字串連接在一起。

|  |  |
| --- | --- |
| 先備知識： | 基本的電腦素養、對 HTML 與 CSS 有基本的認識、對 JavaScript 有認識。 |
| 目標： | 熟悉 JavaScript 字串的基礎。 |

文字的力量
-----

文字對人類而言非常重要——它關乎我們如何交流、溝通。Web 以文字為基底的媒介，它的設計讓人類可以進行交流並分享資訊，因此掌握文字如何在 Web 上呈現是很有用的。HTML 提供文字的結構以及定義；CSS 讓我們更精確地設定樣式；而 JavaScript 則包含許多操作字串的特性，例如：製作客製化的歡迎訊息、正確地顯示所需的文字標籤、排列所需的詞語順序等。

到目前為止，所有我們課程上的編碼幾乎都包含一些字串的操作。

字串 — 基礎
-------

剛開始你會覺得字串與數字的處理方式很類似，但當你越深入就會了解到一些明顯的差異。讓我們從在 console 裡輸入一些基本的程式行來熟悉它吧！在下方，我們提供一個 Console （你也可以另開一個頁籤或視窗使用他 ，或者使用瀏覽器的開發者工具 (en-US)）。
```

<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>JavaScript console</title>
    <style>
 \* {
 box-sizing: border-box;
 }

 html {
 background-color: #0c323d;
 color: #809089;
 font-family: monospace;
 }

 body {
 max-width: 700px;
 }

 p {
 margin: 0;
 width: 1%;
 padding: 0 1%;
 font-size: 16px;
 line-height: 1.5;
 float: left;
 }

 .input p {
 margin-right: 1%;
 }

 .output p {
 width: 100%;
 }

 .input input {
 width: 96%;
 float: left;
 border: none;
 font-size: 16px;
 line-height: 1.5;
 font-family: monospace;
 padding: 0;
 background: #0c323d;
 color: #809089;
 }

 div {
 clear: both;
 }
 </style>
  </head>
  <body></body>

  <script>
 var geval = eval;
 function createInput() {
 var inputDiv = document.createElement("div");
 var inputPara = document.createElement("p");
 var inputForm = document.createElement("input");

 inputDiv.setAttribute("class", "input");
 inputPara.textContent = ">";
 inputDiv.appendChild(inputPara);
 inputDiv.appendChild(inputForm);
 document.body.appendChild(inputDiv);

 if (document.querySelectorAll("div").length > 1) {
 inputForm.focus();
 }

 inputForm.addEventListener("change", executeCode);
 }

 function executeCode(e) {
 try {
 var result = geval(e.target.value);
 } catch (e) {
 var result = "error — " + e.message;
 }

 var outputDiv = document.createElement("div");
 var outputPara = document.createElement("p");

 outputDiv.setAttribute("class", "output");
 outputPara.textContent = "Result: " + result;
 outputDiv.appendChild(outputPara);
 document.body.appendChild(outputDiv);

 e.target.disabled = true;
 e.target.parentNode.style.opacity = "0.5";

 createInput();
 }

 createInput();
 </script>
</html>
```

### 建立字串

1. 首先，先輸入下面幾行程式碼：

```js
let string = "The revolution will not be televised.";
string;
```

就像我們對數字的操作，我們聲明一個變數，並用一個值（字串）來初始化它，而後傳回這個值。唯一的差異在於，你需要用引號包住你的值。
2. 如果你沒有使用引號包住值，或缺少單一邊的引號，都會導致錯誤產生。試著輸入下面幾行程式碼：

```js
let badString = This is a test;
let badString = 'This is a test;
let badString = This is a test';
```

上述的程式無法運作，因為未使用引號包圍的文字都將被視為變數名稱、屬性名稱和保留字等。如果瀏覽器無法辨識它，便會產生錯誤（例如：「missing ; before statement」）。如果瀏覽器可以識別字段從哪裡開始，但無法找到字段的終點，意即缺少第二個引號，則會產生「unterminated string literal」的錯誤。如果你的程式出現了這樣的錯誤，檢查並確認自己的字串是否遺漏了任何引號。
3. 如果你先定義了變數 `string` ，則以下程式碼可以正常運作。馬上來試試看：

```js
let badString = string;
badString;
```

`badString` 會被設定跟 `string` 具有一樣的值。
### 單引號與雙引號

1. 在 JavaScript 中，你可以選擇用單引號或雙引號來包住字串。兩種方式都可行：

```js
let sgl = "Single quotes.";
let dbl = "Double quotes";
sgl;
dbl;
```

2. 兩種之間的差異非常小，取決於你個人的習慣與喜好。你可以選擇一種，並且固定使用它。交互使用兩種方式，容易造成混亂。特別是當你使用兩種不同的引號包住一個字串！這會導致錯誤回傳：

```js
let badQuotes = 'What on earth?";
```

3. 瀏覽器會認為字串並沒有結束，沒有作為包住字串的引號，是可以出現在字串裡面的。看看下面的例子，兩種都是可行的：

```js
let sglDbl = 'Would you eat a "fish supper"?';
let dblSgl = "I'm feeling blue.";
sglDbl;
dblSgl;
```

4. 但是，字串中不可以再使用那個作為包住字串的引號。以下的程式行會出錯，因為瀏覽器無法判斷字串的結尾：

```js
let bigmouth = 'I've got no right to take my place...';
```

This leads us very nicely into our next subject.
### 字串中的跳脫字元（Escaping characters）

要修復先前出錯的那一行程式碼，我們需要解決引號的問題。跳脫字元（Escaping characters）的意思是我們需要確保它們被辨識為文字，而非程式碼本身。在 JavaScript 中，我們在字元的前面放一個反斜線解決這個問題。試試看這個：

```js
let bigmouth = "I've got no right to take my place...";
bigmouth;
```

這是可行的！你可以用一樣的方法跳脫其他字元，例如 `\"`。除此之外，還有一些特殊方法。更詳細的部分，請參閱跳脫符號 。

連接字串（Concatenating strings）
---------------------------

1. 連接（Concatenate）是一個新潮的程式用語。在 JavaScript 中，使用加號（+）將字串連接；這也是我們做數字相加的方式。但在這個狀況下，它有不同的作用。讓我們在 console 中示範：

```js
let one = "Hello, ";
let two = "how are you?";
let joined = one + two;
joined;
```

這邊的結果是 `joined` 這個變數中，有了 「Hello, how are you?」這個值。
2. 在上一個範例中，我們只連接了兩個字串。但只要你在兩個字串之間加上 `+` ，那你要連接幾個都可以。試試看這個：

```js
let multiple = one + one + one + one + two;
multiple;
```

3. 你也可以結合變數和字串。試試看這個：

```js
let response = one + "I am fine — " + two;
response;
```

**備註：** 當你輸入一個字串在你的程式碼中，並用單引號或雙引號將它括起來，它稱為**字串文字**（**string literal**）。

### Concatenation in context

讓我們看看實際運用連接字串的例子——以下是這堂課中稍早的範例：

```html
<button>Press me</button>
```

```js
const button = document.querySelector("button");

button.onclick = function () {
  let name = prompt("What is your name?");
  alert("Hello " + name + ", nice to see you!");
};
```

在程式第四行我們用了 `window.prompt()` (en-US) 這個函式，可以要求使用者透過彈出的對話框去回答問題，並將使用者輸入的文字儲存在給訂的變數內（在這個例子中就是 `name`）。接著我們在第五行用了 `window.alert()` (en-US) 函式，顯示另一個彈出視窗，以連接的方式將兩段字串文字以及 `name` 這個變數結合成一個字串。

### 數字 vs. 字串

1. 那麼我們將字串和數字連接會發生什麼事呢？讓我們在 console 中試試看：

```js
"Front " + 242;
```

或許你預期會跑出錯誤訊息，但看來依然正常運作。若將字串表示成數字似乎不太合理，但將數字表示成字串看來是可行的，所以瀏覽器便會巧妙地將數字轉換成字串，並將這兩個字串連接在一起。
2. 你也可以用兩個數字做這個例子 — 將這兩個數字包在引號中強制將它們轉換成字串。試試看（並使用 typeof 這個運算子去檢查變數是數字或字串）：

```js
let myDate = "19" + "67";
typeof myDate;
```

3. 如果你想轉換數字變數成字串，但不要更動到原本的變數；或是想轉換字串變數成數字，也不要更動到原本的變數，你可以使用以下兩種方式：
    * 物件 `Number` 會將欲處理的變數轉換成數字（如果可行的話）。試試以下例子：
     js
    ```js
    let myString = "123";
    let myNum = Number(myString);
    typeof myNum;

    ```
    * 另一方面，也有 `toString()` (en-US) 方法能夠讓數字轉換為相等的字串。試試看：
     js
    ```js
    let myNum = 123;
    let myString = myNum.toString();
    typeof myString;

    ```這些結構在某些情況相當好用。舉例來說：如果使用者在文字表單中輸入了一個數字，這個數字將會是字串格式。而若想要把這個數字加上另一個數字，那你會希望它是數字格式（才能做數字相加），所以可以使用 `Number()` 來處理這個情況。可以看看實際案例：猜數字遊戲, 第 61 行。
模版字符串(Template literals)
------------------------

另一種你會遇上的字串語法是**模版字符串(template literals)** (也稱做模版字串 template strings)。這是一種更新的語法提供更彈性、簡單的方式去理解字串。

**備註：** 嘗試在你的瀏覽器上測試下面的範例，來看看會得到什麼結果。

將標準字串轉變為模版字符串，你需要將引號 (`' '`, or `" "`) 換為重音符 (backtick characters (`` ``) )，接著來看一個簡單的例子:

```js
let song = "Fight the Youth";
```

轉換成模版字符串會像這樣子:

```js
song = `Fight the Youth`;
```

如果我們想要連接字串，或是將算式的結果包含在裡面，用傳統的字串去寫會很瑣碎且麻煩:

```js
let score = 9;
let highestScore = 10;
let output =
  'I like the song "' +
  song +
  '". I gave it a score of ' +
  (score / highestScore) \* 100 +
  "%.";
```

模版字符串能大量簡化這串程式碼:

```js
output = `I like the song "${song}". I gave it a score of ${
 (score / highestScore) \* 100
}%.`;
```

 全部一串都只需要包含在一對重音符號裡，不再需要切開、合起一堆字串碎片。
 當你想要包含變數或者算式在字串裡時，你只需要將它放在 *佔位符* `${ }` 裡。

你能將複雜的算式包含在模版字符串裡，舉個例子:

```js
let examScore = 45;
let examHighestScore = 70;
examReport = `You scored ${examScore}/${examHighestScore} (${Math.round(
 (examScore / examHighestScore) \* 100,
)}%). ${
 examScore >= 49
 ? "Well done, you passed!"
 : "Bad luck, you didn't pass this time."
}`;
```

* 前兩個佔位符非常好理解，字串裡只包含了單一值。
* 第三個計算了一個百分比的結果且四捨五入進整數。
* 第四個使用了三元運算符來檢查分數是否高於指定分數且印出一個通過或著失敗的訊息結果。

另一個可以注意的點是，如果你想要將傳統字串拆分成多行，你需要加上一個斷行字母, `\n`

```js
output =
  'I like the song "' +
  song +
  '".\nI gave it a score of ' +
  (score / highestScore) \* 100 +
  "%.";
```

 模版字符串保留了程式碼中的斷行方式，所以不再需要使用斷行字母。
 這樣也能達到相同的結果:

```js
output = `I like the song "${song}".
I gave it a score of ${(score / highestScore) \* 100}%.`;
```

 我們建議你盡可能習慣使用模版字符串。現今流行的瀏覽器都能完好的支援它，只有一個地方你能發現它並不支援外: Internet Explorer。
 我們有許多的例子仍然使用目前標準的字串語法，但我們未來將會加入更多模版字符串的應用。

來我們的Template literals 相關頁面看看更多的範例與進階的特色細節。

測試你的技能！
-------

 你已到達文章的結尾了，但你能記得最重要的資訊嗎?
 在繼續學習之前，你可以找些難一點的測驗，來檢測你有記得這些知識 — Test your skills: Strings (en-US). 記住，接下來的文章也需要這些知識，所以你可能想先看看。

結語
--

以上是 JavaScript 中基礎的字串觀念。下個文章中，我們會依循這些概念並試試一些適用於字串的內建方法，進而運用這些方法讓字串能照我們想要的方式呈現。

* 前頁
* Overview: First steps
* 次頁