在代碼中做出決定 - 條件 - 學習該如何開發 Web

在代碼中做出決定 - 條件
=============

* Overview: Building blocks
* 次頁

在任何編程語言中，代碼都需要根據不同的輸入做出決策並相應地執行操作。 例如，在遊戲中，如果玩家的生命數量為 0，則遊戲結束。 在天氣應用程序中，如果在早上查看，則顯示日出圖形; 如果是夜晚，則顯示星星和月亮。 在本文中，我們將探討條件結構如何在 JavaScript 中工作。

|  |  |
| --- | --- |
| Prerequisites: |
 Basic computer literacy, a basic understanding of HTML and CSS,
 JavaScript first steps.
  |
| Objective: | To understand how to use conditional structures in JavaScript. |

你可以擁有一個條件..!
------------

從小到大，人們(和其它動物)作出決定的時間會影響到他們的生活 ("我應該吃一個或兩個餅乾?") ("我應該留在我的家鄉並在我父親的農場工作還是應該要到美國研讀天體物理學?")

條件敘述句（Conditional statements）讓我們能將這些決定的過程在 Javascript 表示出來，從一定得做出的選擇（例如：「吃一個或兩個餅乾」），到這些選擇的結果（或許「吃一個餅乾」會出現「還是會餓」這種結果，而「吃兩個餅乾」的結果會是「吃飽了，但因為吃了全部餅乾而被媽媽罵」）。

![](/zh-TW/docs/Learn/JavaScript/Building_blocks/conditionals/cookie-choice-small.png)

if ... else 敘述句
---------------

讓我們來看 Javascript 中最常見的條件敘述句 `if ... else` statement.

### 基本的 if ... else 語法

最基本的 `if...else` 語法看起來像以下 虛擬碼:
```

if (condition) {
  code to run if condition is true
} else {
  run some other code instead
}
```

這邊我們可以得知基礎的架構：

1. 關鍵字 `if` 和後頭的括號。
2. 想測試的條件放在括號中（通常像是「這個值是否大於其他值」或是「這個值是否存在」等等）。這裡的條件會使用先前提過的 比較運算子（comparison operators），並且最後會回傳 `true` 或是 `false`。
3. 第一組大括號，在大括號裡面有一些程式碼 — 內容可以是任何我們所需要執行的程式碼，並且只有在條件句回傳 `true` 才會執行。
4. 關鍵字 `else`。
5. 另一組大括號，在大括號中我們一樣是放置所需的程式碼，並只有在條件句回傳 `false` 才會執行。

這個程式碼的架構很容易理解 — 「如果條件回傳 `true` ，則執行程式 A，否則執行程式 B」。

值得注意的是，`else` 和第二組大括號並不是必要的。如以下範例也能夠執行：
```

if (condition) {
  code to run if condition is true
}

run some other code
```

然而，在這邊有一點要注意：在這個例子中的第二個區塊並沒有被條件式控制，也就是說無論條件式回傳的是 `true` 或是 `false`，它都會執行。這並不一定是件壞事，但它可能不會是你要的，通常你可能是想要執行程式碼的一個區塊或是另一塊，而不是兩個都執行。

最後一點，你可能有時候會看到 `if...else` 敘述是不加大括弧的：
```

if (condition) code to run if condition is true
else run some other code instead
```

這當然也是有效的程式碼，但不太建議這樣用。使用大括弧能夠很清楚地看到程式區塊、縮排，也能夠擁有多行程式碼，對於程式的可讀性會提高許多。

### A real example

To understand this syntax better, let's consider a real example. Imagine a child being asked for help with a chore by their mother or father. The parent might say "Hey sweetheart, if you help me by going and doing the shopping, I'll give you some extra allowance so you can afford that toy you wanted." In JavaScript, we could represent this like so:

```js
var shoppingDone = false;

if (shoppingDone === true) {
  var childsAllowance = 10;
} else {
  var childsAllowance = 5;
}
```

This code as shown will always result in the `shoppingDone` variable returning `false`, meaning disappointment for our poor child. It'd be up to us to provide a mechanism for the parent to set the `shoppingDone` variable to `true` if the child did the shopping.

**備註：** You can see a more complete version of this example on GitHub (also see it running live.)

### else if

The last example provided us with two choices, or outcomes — but what if we want more than two?

There is a way to chain on extra choices/outcomes to your `if...else` — using `else if`. Each extra choice requires an additional block to put in between `if() { ... }` and `else { ... }` — check out the following more involved example, which could be part of a simple weather forecast application:

```html
<label for="weather">Select the weather type today: </label>
<select id="weather">
  <option value="">--Make a choice--</option>
  <option value="sunny">Sunny</option>
  <option value="rainy">Rainy</option>
  <option value="snowing">Snowing</option>
  <option value="overcast">Overcast</option>
</select>

<p></p>
```

```js
var select = document.querySelector("select");
var para = document.querySelector("p");

select.addEventListener("change", setWeather);

function setWeather() {
  var choice = select.value;

  if (choice === "sunny") {
    para.textContent =
      "It is nice and sunny outside today. Wear shorts! Go to the beach, or the park, and get an ice cream.";
  } else if (choice === "rainy") {
    para.textContent =
      "Rain is falling outside; take a rain coat and a brolly, and don't stay out for too long.";
  } else if (choice === "snowing") {
    para.textContent =
      "The snow is coming down — it is freezing! Best to stay in with a cup of hot chocolate, or go build a snowman.";
  } else if (choice === "overcast") {
    para.textContent =
      "It isn't raining, but the sky is grey and gloomy; it could turn any minute, so take a rain coat just in case.";
  } else {
    para.textContent = "";
  }
}
```

1. Here we've got an HTML `<select>` (en-US) element allowing us to make different weather choices, and a simple paragraph.
2. In the JavaScript, we are storing a reference to both the `<select>` (en-US) and `<p>` (en-US) elements, and adding an event listener to the `<select>` element so that when its value is changed, the `setWeather()` function is run.
3. When this function is run, we first set a variable called `choice` to the current value selected in the `<select>` element. We then use a conditional statement to show different text inside the paragraph depending on what the value of `choice` is. Notice how all the conditions are tested in `else if() {...}` blocks, except for the first one, which is tested in an `if() {...} block`.
4. The very last choice, inside the `else {...}` block, is basically a "last resort" option — the code inside it will be run if none of the conditions are `true`. In this case, it serves to empty the text out of the paragraph if nothing is selected, for example if a user decides to re-select the "--Make a choice--" placeholder option shown at the beginning.

**備註：** You can also find this example on GitHub (see it running live on there also.)

### A note on comparison operators

Comparison operators are used to test the conditions inside our conditional statements. We first looked at comparison operators back in our Basic math in JavaScript — numbers and operators article. Our choices are:

* `===` and `!==` — test if one value is identical to, or not identical to, another.
* `<` and `>` — test if one value is less than or greater than another.
* `<=` and `>=` — test if one value is less than or equal to, or greater than or equal to, another.

**備註：** Review the material at the previous link if you want to refresh your memories on these.

We wanted to make a special mention of testing boolean (`true`/`false`) values, and a common pattern you'll come across again and again. Any value that is not `false`, `undefined`, `null`, `0`, `NaN`, or an empty string (`''`) actually returns `true` when tested as a conditional statement, therefore you can simply use a variable name on its own to test whether it is `true`, or even that it exists (i.e. it is not undefined.) So for example:

```js
var cheese = "Cheddar";

if (cheese) {
  console.log("Yay! Cheese available for making cheese on toast.");
} else {
  console.log("No cheese on toast for you today.");
}
```

And, returning to our previous example about the child doing a chore for their parent, you could write it like this:

```js
var shoppingDone = false;

if (shoppingDone) {
  // don't need to explicitly specify '=== true'
  var childsAllowance = 10;
} else {
  var childsAllowance = 5;
}
```

### Nesting if ... else

It is perfectly OK to put one `if...else` statement inside another one — to nest them. For example, we could update our weather forecast application to show a further set of choices depending on what the temperature is:

```js
if (choice === "sunny") {
  if (temperature < 86) {
    para.textContent =
      "It is " +
      temperature +
      " degrees outside — nice and sunny. Let's go out to the beach, or the park, and get an ice cream.";
  } else if (temperature >= 86) {
    para.textContent =
      "It is " +
      temperature +
      " degrees outside — REALLY HOT! If you want to go outside, make sure to put some suncream on.";
  }
}
```

Even though the code all works together, each `if...else` statement works completely independently of the other one.

### Logical operators: AND, OR and NOT

If you want to test multiple conditions without writing nested `if...else` statements, logical operators (en-US) can help you. When used in conditions, the first two do the following:

* `&&` — AND; allows you to chain together two or more expressions so that all of them have to individually evaluate to `true` for the whole expression to return `true`.
* `||` — OR; allows you to chain together two or more expressions so that one or more of them have to individually evaluate to `true` for the whole expression to return `true`.

To give you an AND example, the previous example snippet can be rewritten to this:

```js
if (choice === "sunny" && temperature < 86) {
  para.textContent =
    "It is " +
    temperature +
    " degrees outside — nice and sunny. Let's go out to the beach, or the park, and get an ice cream.";
} else if (choice === "sunny" && temperature >= 86) {
  para.textContent =
    "It is " +
    temperature +
    " degrees outside — REALLY HOT! If you want to go outside, make sure to put some suncream on.";
}
```

So for example, the first code block will only be run if `choice === 'sunny'` *and* `temperature < 86` return `true`.

Let's look at a quick OR example:

```js
if (iceCreamVanOutside || houseStatus === "on fire") {
  console.log("You should leave the house quickly.");
} else {
  console.log("Probably should just stay in then.");
}
```

The last type of logical operator, NOT, expressed by the `!` operator, can be used to negate an expression. Let's combine it with OR in the above example:

```js
if (!(iceCreamVanOutside || houseStatus === "on fire")) {
  console.log("Probably should just stay in then.");
} else {
  console.log("You should leave the house quickly.");
}
```

In this snippet, if the OR statement returns `true`, the NOT operator will negate it so that the overall expression returns `false`.

You can combine as many logical statements together as you want, in whatever structure. The following example executes the code inside only if both OR statements return true, meaning that the overall AND statement will return true:

```js
if ((x === 5 || y > 3 || z <= 10) && (loggedIn || userName === "Steve")) {
  // run the code
}
```

A common mistake when using the logical OR operator in conditional statements is to try to state the variable whose value you are checking once, and then give a list of values it could be to return true, separated by `||` (OR) operators. For example:

```js
if (x === 5 || 7 || 10 || 20) {
  // run my code
}
```

In this case the condition inside `if(...)` will always evaluate to true since 7 (or any other non-zero value) always evaluates to true. This condition is actually saying "if x equals 5, or 7 is true — which it always is". This is logically not what we want! To make this work you've got to specify a complete test either side of each OR operator:

```js
if (x === 5 || x === 7 || x === 10 || x === 20) {
  // run my code
}
```

switch statements
-----------------

`if...else` statements do the job of enabling conditional code well, but they are not without their downsides. They are mainly good for cases where you've got a couple of choices, and each one requires a reasonable amount of code to be run, and/or the conditions are complex (e.g. multiple logical operators). For cases where you just want to set a variable to a certain choice of value or print out a particular statement depending on a condition, the syntax can be a bit cumbersome, especially if you've got a large number of choices.

`switch` statements are your friend here — they take a single expression/value as an input, and then look through a number of choices until they find one that matches that value, executing the corresponding code that goes along with it. Here's some more pseudocode, to give you an idea:
```

switch (expression) {
  case choice1:
    run this code
    break;

  case choice2:
    run this code instead
    break;

  // include as many cases as you like

  default:
    actually, just run this code
}
```

Here we've got:

1. The keyword `switch`, followed by a set of parentheses.
2. An expression or value inside the parentheses.
3. The keyword `case`, followed by a choice that the expression/value could be, followed by a colon.
4. Some code to run if the choice matches the expression.
5. A `break` statement, followed by a semi-colon. If the previous choice matches the expression/value, the browser stops executing the code block here, and moves on to any code that appears below the switch statement.
6. As many other cases (bullets 3–5) as you like.
7. The keyword `default`, followed by exactly the same code pattern as one of the cases (bullets 3–5), except that `default` does not have a choice after it, and you don't need to `break` statement as there is nothing to run after this in the block anyway. This is the default option that runs if none of the choices match.

**備註：** You don't have to include the `default` section — you can safely omit it if there is no chance that the expression could end up equaling an unknown value. If there is a chance of this however, you need to include it to handle unknown cases.

### A switch example

Let's have a look at a real example — we'll rewrite our weather forecast application to use a switch statement instead:

```html
<label for="weather">Select the weather type today: </label>
<select id="weather">
  <option value="">--Make a choice--</option>
  <option value="sunny">Sunny</option>
  <option value="rainy">Rainy</option>
  <option value="snowing">Snowing</option>
  <option value="overcast">Overcast</option>
</select>

<p></p>
```

```js
var select = document.querySelector("select");
var para = document.querySelector("p");

select.addEventListener("change", setWeather);

function setWeather() {
  var choice = select.value;

  switch (choice) {
    case "sunny":
      para.textContent =
        "It is nice and sunny outside today. Wear shorts! Go to the beach, or the park, and get an ice cream.";
      break;
    case "rainy":
      para.textContent =
        "Rain is falling outside; take a rain coat and a brolly, and don't stay out for too long.";
      break;
    case "snowing":
      para.textContent =
        "The snow is coming down — it is freezing! Best to stay in with a cup of hot chocolate, or go build a snowman.";
      break;
    case "overcast":
      para.textContent =
        "It isn't raining, but the sky is grey and gloomy; it could turn any minute, so take a rain coat just in case.";
      break;
    default:
      para.textContent = "";
  }
}
```

**備註：** You can also find this example on GitHub (see it running live on there also.)

三元運算符
-----

There is one final bit of syntax we want to introduce you to, before we get you to play with some examples. The ternary or conditional operator is a small bit of syntax that tests a condition and returns one value/expression if it is `true`, and another if it is `false` — this can be useful in some situations, and can take up a lot less code than an `if...else` block if you simply have two choices that are chosen between via a `true`/`false` condition. The pseudocode looks like this:
```

( condition ) ? run this code : run this code instead
```

So let's look at a simple example:

```js
var greeting = isBirthday
  ? "Happy birthday Mrs. Smith — we hope you have a great day!"
  : "Good morning Mrs. Smith.";
```

Here we have a variable called `isBirthday` — if this is `true`, we give our guest a happy birthday message; if not, we give her the standard daily greeting.

### Ternary operator example

You don't just have to set variable values with the ternary operator; you can also run functions, or lines of code — anything you like. The following live example shows a simple theme chooser where the styling for the site is applied using a ternary operator.

```html
<label for="theme">Select theme: </label>
<select id="theme">
  <option value="white">White</option>
  <option value="black">Black</option>
</select>

<h1>This is my website</h1>
```

```js
var select = document.querySelector("select");
var html = document.querySelector("html");
document.body.style.padding = "10px";

function update(bgColor, textColor) {
  html.style.backgroundColor = bgColor;
  html.style.color = textColor;
}

select.onchange = function () {
  select.value === "black"
    ? update("black", "white")
    : update("white", "black");
};
```

Here we've got a `<select>` (en-US) element to choose a theme (black or white), plus a simple `<h1>` (en-US) to display a website title. We also have a function called `update()`, which takes two colors as parameters (inputs). The website's background color is set to the first provided color, and its text color is set to the second provided color.

Finally, we've also got an onchange (en-US) event listener that serves to run a function containing a ternary operator. It starts with a test condition — `select.value === 'black'`. If this returns `true`, we run the `update()` function with parameters of black and white, meaning that we end up with background color of black and text color of white. If it returns `false`, we run the `update()` function with parameters of white and black, meaning that the site color are inverted.

**備註：** You can also find this example on GitHub (see it running live on there also.)

Active learning: A simple calendar
----------------------------------

In this example you are going to help us finish a simple calendar application. In the code you've got:

* A `<select>` (en-US) element to allow the user to choose between different months.
* An `onchange` event handler to detect when the value selected in the `<select>` menu is changed.
* A function called `createCalendar()` that draws the calendar and displays the correct month in the `<h1>` (en-US) element.

We need you to write a conditional statement inside the `onchange` handler function, just below the `// ADD CONDITIONAL HERE` comment. It should:

1. Look at the selected month (stored in the `choice` variable. This will be the `<select>` element value after the value changes, so "January" for example.)
2. Set a variable called `days` to be equal to the number of days in the selected month. To do this you'll have to look up the number of days in each month of the year. You can ignore leap years for the purposes of this example.

Hints:

* You are advised to use logical OR to group multiple months together into a single condition; many of them share the same number of days.
* Think about which number of days is the most common, and use that as a default value.

If you make a mistake, you can always reset the example with the "Reset" button. If you get really stuck, press "Show solution" to see a solution.
```

<h2>Live output</h2>
<div class="output" style="height: 500px;overflow: auto;">
  <label for="month">Select month: </label>
  <select id="month">
    <option value="January">January</option>
    <option value="February">February</option>
    <option value="March">March</option>
    <option value="April">April</option>
    <option value="May">May</option>
    <option value="June">June</option>
    <option value="July">July</option>
    <option value="August">August</option>
    <option value="September">September</option>
    <option value="October">October</option>
    <option value="November">November</option>
    <option value="December">December</option>
  </select>

  <h1></h1>

  <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: 400px;width: 95%">
var select = document.querySelector('select');
var list = document.querySelector('ul');
var h1 = document.querySelector('h1');

select.onchange = function() {
  var choice = select.value;

  // ADD CONDITIONAL HERE

  createCalendar(days, choice);
}

function createCalendar(days, choice) {
  list.innerHTML = '';
  h1.textContent = choice;
  for (var i = 1; i <= days; i++) {
    var listItem = document.createElement('li');
    listItem.textContent = i;
    list.appendChild(listItem);
  }
}

createCalendar(31,'January');
</textarea>

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

```
.output \* {
  box-sizing: border-box;
}

.output ul {
  padding-left: 0;
}

.output li {
  display: block;
  float: left;
  width: 25%;
  border: 2px solid white;
  padding: 5px;
  height: 40px;
  background-color: #4a2db6;
  color: white;
}

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 select = document.querySelector('select');\nvar list = document.querySelector('ul');\nvar h1 = document.querySelector('h1');\n\nselect.onchange = function() {\n var choice = select.value;\n var days = 31;\n if(choice === 'February') {\n days = 28;\n } else if(choice === 'April' || choice === 'June' || choice === 'September'|| choice === 'November') {\n days = 30;\n }\n\n createCalendar(days, choice);\n}\n\nfunction createCalendar(days, choice) {\n list.innerHTML = '';\n h1.textContent = choice;\n for(var i = 1; i <= days; i++) {\n var listItem = document.createElement('li');\n listItem.textContent = i;\n list.appendChild(listItem);\n }\n }\n\ncreateCalendar(31,'January');";
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();
};
```

Active learning: More color choices
-----------------------------------

In this example you are going to take the ternary operator example we saw earlier and convert the ternary operator into a switch statement that will allow us to apply more choices to the simple website. Look at the `<select>` (en-US) — this time you'll see that it has not two theme options, but five. You need to add a switch statement just underneath the `// ADD SWITCH STATEMENT` comment:

* It should accept the `choice` variable as its input expression.
* For each case, the choice should equal one of the possible values that can be selected, i.e. white, black, purple, yellow, or psychedelic.
* For each case, the `update()` function should be run, and be passed two color values, the first one for the background color, and the second one for the text color. Remember that color values are strings, so need to be wrapped in quotes.

If you make a mistake, you can always reset the example with the "Reset" button. If you get really stuck, press "Show solution" to see a solution.
```

<h2>Live output</h2>
<div class="output" style="height: 300px;">
  <label for="theme">Select theme: </label>
  <select id="theme">
    <option value="white">White</option>
    <option value="black">Black</option>
    <option value="purple">Purple</option>
    <option value="yellow">Yellow</option>
    <option value="psychedelic">Psychedelic</option>
  </select>

  <h1>This is my website</h1>
</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: 450px;width: 95%">
var select = document.querySelector('select');
var html = document.querySelector('.output');

select.onchange = function() {
  var choice = select.value;

  // ADD SWITCH STATEMENT
}

function update(bgColor, textColor) {
  html.style.backgroundColor = bgColor;
  html.style.color = textColor;
}</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 select = document.querySelector('select');\nvar html = document.querySelector('.output');\n\nselect.onchange = function() {\n var choice = select.value;\n\n switch(choice) {\n case 'black':\n update('black','white');\n break;\n case 'white':\n update('white','black');\n break;\n case 'purple':\n update('purple','white');\n break;\n case 'yellow':\n update('yellow','darkgray');\n break;\n case 'psychedelic':\n update('lime','purple');\n break;\n }\n}\n\nfunction update(bgColor, textColor) {\n html.style.backgroundColor = bgColor;\n html.style.color = textColor;\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
----------

And that's all you really need to know about conditional structures in JavaScript right now! I'm sure you'll have understood these concepts and worked through the examples with ease; if there is anything you didn't understand, feel free to read through the article again, or contact us to ask for help.

See also
--------

* Comparison operators
* Conditional statements in detail
* if...else reference
* Conditional (ternary) operator reference
* Overview: Building blocks
* 次頁


影像圖庫 - 學習該如何開發 Web

影像圖庫
====

* 前頁  (en-US)
* Overview: Building blocks

現在我們已經看過了基本的 JavaScript 組建，我們將讓你做一個測試，從建立一個在很多網站上常見的事物 — JavaScript 基礎的影像圖庫，來測試你對迴圈、函數、條件式及事件的知識。

|  |  |
| --- | --- |
| 先修課程： | 在進行這個評量前，你應已閱讀、練習了本模組中所有的文章。 |
| 目的： | 測試對JavaScript中迴圈、函數、條件式及事件的瞭解程度。 |

從這裡開始
-----

要進行這個評量，你要先下載 grab the ZIP 檔案，解壓縮在你電腦中的某個檔案夾作為範例。

**備註：** 你也可以在某些網站進行評鑑，如 JSBin 或 Glitch。你可以把這些 HTML、CSS 和 JavaScript 貼到這些線上編輯器中。如果你用了一個沒法把 JavaScript/CSS 分別放在不同面板的線上編輯器，你可以放心的把這些`<script>`/`<style>`元件改成 inline 貼進 HTML 網頁裡。

專案簡報
----

你手上已有我們提供的一些 HTML、CSS 和圖片資料，以及幾行 JavaScript 程式碼；你要寫一些必要的 JavaScript 讓它變成可運作的程式。這些 HTML 的 body 看起來如下：

```html
<h1>Image gallery example</h1>

<div class="full-img">
  <img class="displayed-img" src="images/pic1.jpg" />
  <div class="overlay"></div>
  <button class="dark">Darken</button>
</div>

<div class="thumb-bar"></div>
```

完成後看起來像下圖：

![](/zh-TW/docs/Learn/JavaScript/Building_blocks/Image_gallery/gallery.png)

範例 CSS 檔案中最有趣的部分是：

* 在 `full-img <div>` 裡有的三個組成元素使用絕對位置 — `<img>` 展示全尺寸圖片，在它正上方有個與它尺寸相同的空 `<div>` （用來放置用半透明的背景色彩讓圖片產生變暗效果的層），然後 `<button>` 用來控制變暗效果。
* 設定 `thumb-bar <div>` （一般叫做「縮圖」的圖片）裡的圖片讓它縮小成原來的 20%，之後讓它浮（float）在左邊讓縮圖能一個個相鄰的排成一列。

在你的 JavaScript 裡需要：

* 讓所有圖片迴圈（Loop） through all the images, 在 `thumb-bar <div>` 裡的每個 `<img>` 塞進一個 `<img>` ，讓圖片內嵌在頁面上。
* 在 `thumb-bar <div>` 裡的每個 `<img>` 添加一個 `onclick` 處理器使這個圖片被點擊時會放大展示在 `displayed-img <img>` 裡。
* 在 `<button>` 添加一個 `onclick` 處理器，當點擊時，全尺寸圖片產生暗化效果，再次點擊時移除暗化效果。

為了讓你更清楚，你可以看看這個 完成的範例 （但別偷看原始碼！）

一步步完成
-----

接下來幾節描述你該怎麼做。

### 讓所有圖片迴圈

我們已提供了幾行程式碼：將`thumb-bar`和 `<div>`儲存在 `thumbBar`這個變數裡、建立一個新的 `<img>` 元件、將它的 `src` 屬性設定在一個值為 `xxx` 的佔位符中，以及在 `thumbBar` 裡增加新 `<img>` 。

你要做的是：

1. Put the section of code below the "Looping through images" comment inside a loop that loops through all 5 images — you just need to loop through five numbers, one representing each image.
2. In each loop iteration, replace the `xxx` placeholder value with a string that will equal the path to the image in each case. We are setting the value of the `src` attribute to this value in each case. Bear in mind that in each case, the image is inside the images directory and its name is `pic1.jpg`, `pic2.jpg`, etc.
### 在每個縮圖上添加 onclick 事件處理器

In each loop iteration, you need to add an `onclick` handler to the current `newImage` — this should:

1. 在每個 `<img>` 中把`"src"`作為運行`getAttribute()` 函數的參數，取得現在這張圖片的 `src` 屬性的值。但是要怎麼抓到現在這張圖片？如果用`newImage` 是做不到的，當在添加事件處理器前，迴圈已經先完成了；所以你每次都獲得前一個 `<img>`的回傳的 `src` 值。解法是，記住，在每個事件中，事件處理器的目標是 `<img>` ，如何獲得事件物件的資訊呢？
2. Run a function, passing it the returned `src` value as a parameter. You can call this function whatever you like.
3. This event handler function should set the `src` attribute value of the `displayed-img <img>` to the `src` value passed in as a parameter. We've already provided you with a line that stores a reference to the relevant `<img>` in a variable called `displayedImg`. Note that we want a defined named function here.
### 寫一個讓暗化/亮化按鈕可以運作的處理器

That just leaves our darken/lighten `<button>` — we've already provided a line that stores a reference to the `<button>` in a variable called `btn`. You need to add an `onclick` handler that:

1. Checks the current class name set on the `<button>` — you can again achieve this by using `getAttribute()`.
2. If the class name is `"dark"`, changes the `<button>` class to `"light"` (using `setAttribute()` (en-US)), its text content to "Lighten", and the `background-color` of the overlay `<div>` to `"rgba(0,0,0,0.5)"`.
3. If the class name not `"dark"`, changes the `<button>` class to `"dark"`, its text content back to "Darken", and the `background-color` of the overlay `<div>` to `"rgba(0,0,0,0)"`.

The following lines provide a basis for achieving the changes stipulated in points 2 and 3 above.

```js
btn.setAttribute("class", xxx);
btn.textContent = xxx;
overlay.style.backgroundColor = xxx;
```

提醒與提示
-----

* 你完全不需要去編輯任何的 HTML 或 CSS。
評量
--

If you are following this assessment as part of an organized course, you should be able to give your work to your teacher/mentor for marking. If you are self-learning, then you can get the marking guide fairly easily by asking on the Learning Area Discourse thread, or in the #mdn IRC channel on Mozilla IRC. Try the exercise first — there is nothing to be gained by cheating!

* 前頁  (en-US)
* Overview: Building blocks


函數 - 可重複使用的程式碼區塊 - 學習該如何開發 Web

函數 - 可重複使用的程式碼區塊
================

* 前頁
* Overview: Building blocks
* 次頁  (en-US)

程式設計的另一個基本概念是函數，它允許你儲存一段程式碼，該程式碼在定義的區塊內執行單個任務，然後在需要時使用一個簡短命令調用該程式碼區塊，而不必多次輸入相同的程式碼。 在本文中，我們將探索函數背後的基本概念，例如基本語法、如何調用和定義它們、作用域範圍與參數。

|  |  |
| --- | --- |
| 先備知識： | 基礎電腦術語、對 HTML 及 CSS 有基本認識、JavaScript 的第一步。 |
| 學習目標： | 了解 JavaScript 函數背後的基本概念。 |

我在哪能找到函數？
---------

在 JavaScript 裡，你到處都能看到函數的蹤影。事實上，我們在前面的課程中一直都在用函數，只是沒什麼提及而已。如今是時候讓我們詳細探討函數並認真探索它們的語法了。

幾乎每當你使用了包含一對小括號 — `()` — 的 JavaScritp 結構，並且沒有用到諸如 for 迴圈、while 與 do...while 迴圈或 if...else 敘述等常見的內建語言結構時，你就是在使用函數。

瀏覽器內建函數
-------

到目前為止，我們已在課程中大量使用了瀏覽器內建的函數。例如，每當我們操控一個字串時：

```js
var myText = "I am a string";
var newString = myText.replace("string", "sausage");
console.log(newString);
// 字串的 replace() 函數會先替換一段子字串，
// 再回傳替換過後的新字串
```

或是每當我們操控一個陣列時：

```js
var myArray = ["I", "love", "chocolate", "frogs"];
var madeAString = myArray.join(" ");
console.log(madeAString);
// 陣列的 join() 函數會先將所有陣列元素合併，
// 形成一個新字串，再回傳該新字串
```

或是每當我們產生一個亂數：

```js
var myNumber = Math.random();
// random() 函數會先產生一個介於 0 到 1 之間
// 的亂數，再回傳該數字
```

……我們其實當在使用函數。

**備註：** 若有需要，你可以瀏覽器的 JavaScript 主控台中輸入以上指令碼，藉此重新熟悉這些功能。

JavaScript 語言有很多內建函數，讓你不用重覆寫所有程式碼就能做很多事。事實上，在你執行瀏覽器的內建函數時，你呼叫到的某些程式碼並不能用 JavaScript 來寫——很多這種函數是在部分呼叫瀏覽器背景語言，大多是由 C++ 這種低階系統語言寫成，而非 JavaScript 這種網際網路語言。

麻煩謹記在心，有些瀏覽器內建函數不是 JavaScript 語言核心的一部份——有些被定義為瀏覽器 API 的一部份，它們已預設語言為基礎來提供更多功能（先前的章節裡有更多說明）。在未來的模組中我們會更深入探討瀏覽器 API 的用法。

函數（function） vs 方法（method）
--------------------------

在我們繼續前，有件事需要先釐清——技術上來說，瀏覽器內建函數並不是函數（function）——它們是**方法**（method）。這聽起來有點令人疑惑，不過別擔心——在你目前的學習階段，「函數」與「方法」這兩個詞彙大多時候是可以互換的，至少對我們的目標而言。

這兩個詞的區別在方法（method）是定義在物件裡的函數（function）。瀏覽器內建函數（方法）和變數（此處被稱作**屬性**（property））被儲存在建構好的物件裡，令程式碼控制起來更有效率也更簡單。

你還不需要了解 JavaScript 物件的內部運作——在之後的模組我們會學到物件內部的所有運作方式，以及如何創建你自己的物件。此時，我們只需要釐清方法與函數之間任何可能搞混的地方——在你上網查相關教學資源時很可能兩個詞彙都會碰到。

自訂函數
----

You've also seen a lot of **custom functions** in the course so far — functions defined in your code, not inside the browser. Anytime you saw a custom name with parentheses straight after it, you were using a custom function. In our random-canvas-circles.html example (see also the full source code) from our loops article, we included a custom `draw()` function that looked like this:

```js
function draw() {
  ctx.clearRect(0, 0, WIDTH, HEIGHT);
  for (var i = 0; i < 100; i++) {
    ctx.beginPath();
    ctx.fillStyle = "rgba(255,0,0,0.5)";
    ctx.arc(random(WIDTH), random(HEIGHT), random(50), 0, 2 \* Math.PI);
    ctx.fill();
  }
}
```

This function draws 100 random circles inside an `<canvas>` element. Every time we want to do that, we can just invoke the function with this

```js
draw();
```

rather than having to write all that code out again every time we want to repeat it. And functions can contain whatever code you like — you can even call other functions from inside functions. The above function for example calls the `random()` function three times, which is defined by the following code:

```js
function random(number) {
  return Math.floor(Math.random() \* number);
}
```

We needed this function because the browser's built-in Math.random() function only generates a random decimal number between 0 and 1. We wanted a random whole number between 0 and a specified number.

呼叫函數
----

You are probably clear on this by now, but just in case ... to actually use a function after it has been defined, you've got to run — or invoke — it. This is done by including the name of the function in the code somewhere, followed by parentheses.

```js
function myFunction() {
  alert("hello");
}

myFunction();
// 呼叫一次函數
```

匿名函數
----

You may see functions defined and invoked in slightly different ways. So far we have just created a function like so:

```js
function myFunction() {
  alert("hello");
}
```

But you can also create a function that doesn't have a name:

```js
function() {
  alert('hello');
}
```

This is called an **anonymous function** — it has no name! It also won't do anything on its own. You generally use an anonymous function along with an event handler, for example the following would run the code inside the function whenever the associated button is clicked:

```js
var myButton = document.querySelector("button");

myButton.onclick = function () {
  alert("hello");
};
```

The above example would require there to be a `<button>` (en-US) element available on the page to select and click. You've already seen this structure a few times throughout the course, and you'll learn more about and see it in use in the next article.

You can also assign an anonymous function to be the value of a variable, for example:

```js
var myGreeting = function () {
  alert("hello");
};
```

This function could now be invoked using:

```js
myGreeting();
```

This effectively gives the function a name; you can also assign the function to be the value of multiple variables, for example:

```js
var anotherGreeting = function () {
  alert("hello");
};
```

This function could now be invoked using either of

```js
myGreeting();
anotherGreeting();
```

But this would just be confusing, so don't do it! When creating functions, it is better to just stick to this form:

```js
function myGreeting() {
  alert("hello");
}
```

You will mainly use anonymous functions to just run a load of code in response to an event firing — like a button being clicked — using an event handler. Again, this looks something like this:

```js
myButton.onclick = function () {
  alert("hello");
  // 這邊你的程式碼想寫多長就寫多長
};
```

函數參數
----

Some functions require **parameters** to be specified when you are invoking them — these are values that need to be included inside the function parentheses, which it needs to do its job properly.

**備註：** Parameters are sometimes called arguments, properties, or even attributes.

As an example, the browser's built-in Math.random() function doesn't require any parameters. When called, it always returns a random number between 0 and 1:

```js
var myNumber = Math.random();
```

The browser's built-in string replace() (en-US) function however needs two parameters — the substring to find in the main string, and the substring to replace that string with:

```js
var myText = "I am a string";
var newString = myText.replace("string", "sausage");
```

**備註：** When you need to specify multiple parameters, they are separated by commas.

It should also be noted that sometimes parameters are optional — you don't have to specify them. If you don't, the function will generally adopt some kind of default behavior. As an example, the array join() function's parameter is optional:

```js
var myArray = ["I", "love", "chocolate", "frogs"];
var madeAString = myArray.join(" ");
// 回傳 'I love chocolate frogs'
var madeAString = myArray.join();
// 回傳 'I,love,chocolate,frogs'
```

If no parameter is included to specify a joining/delimiting character, a comma is used by default.

函數作用域及衝突
--------

Let's talk a bit about scope (en-US) — a very important concept when dealing with functions. When you create a function, the variables and other things defined inside the function are inside their own separate **scope**, meaning that they are locked away in their own separate compartments, unreachable from inside other functions or from code outside the functions.

The top level outside all your functions is called the **global scope**. Values defined in the global scope are accessible from everywhere in the code.

JavaScript is set up like this for various reasons — but mainly because of security and organization. Sometimes you don't want variables to be accessible from everywhere in the code — external scripts that you call in from elsewhere could start to mess with your code and cause problems because they happen to be using the same variable names as other parts of the code, causing conflicts. This might be done maliciously, or just by accident.

For example, say you have an HTML file that is calling in two external JavaScript files, and both of them have a variable and a function defined that use the same name:

```html
<!-- Excerpt from my HTML -->
<script src="first.js"></script>
<script src="second.js"></script>
<script>
 greeting();
</script>
```

```js
// first.js
var name = "Chris";
function greeting() {
  alert("Hello " + name + ": welcome to our company.");
}
```

```js
// second.js
var name = "Zaptec";
function greeting() {
  alert("Our company is called " + name + ".");
}
```

Both functions you want to call are called `greeting()`, but you can only ever access the `second.js` file's `greeting()` function — it is applied to the HTML later on in the source code, so its variable and function overwrite the ones in `first.js`.

**備註：** You can see this example running live on GitHub (see also the source code).

Keeping parts of your code locked away in functions avoids such problems, and is considered best practice.

It is a bit like a zoo. The lions, zebras, tigers, and penguins are kept in their own enclosures, and only have access to the things inside their enclosures — in the same manner as the function scopes. If they were able to get into other enclosures, problems would occur. At best, different animals would feel really uncomfortable inside unfamiliar habitats — a lion or tiger would feel terrible inside the penguins' watery, icy domain. At worst, the lions and tigers might try to eat the penguins!

![](/zh-TW/docs/Learn/JavaScript/Building_blocks/Functions/mdn-mozilla-zoo.png)

The zoo keeper is like the global scope — he or she has the keys to access every enclosure, to restock food, tend to sick animals, etc.

### 互動學習：玩轉作用域

Let's look at a real example to demonstrate scoping.

1. First, make a local copy of our function-scope.html example. This contains two functions called `a()` and `b()`, and three variables — `x`, `y`, and `z` — two of which are defined inside the functions, and one in the global scope. It also contains a third function called `output()`, which takes a single parameter and outputs it in a paragraph on the page.
2. Open the example up in a browser and in your text editor.
3. Open the JavaScript console in your browser developer tools. In the JavaScript console, enter the following command:

```js
output(x);
```

You should see the value of variable `x` output to the screen.
4. Now try entering the following in your console

```js
output(y);
output(z);
```

Both of these should return an error along the lines of "ReferenceError: y is not defined". Why is that? Because of function scope — `y` and `z` are locked inside the `a()` and `b()` functions, so `output()` can't access them when called from the global scope.
5. However, what about when it's called from inside another function? Try editing `a()` and `b()` so they look like this:

```js
function a() {
  var y = 2;
  output(y);
}

function b() {
  var z = 3;
  output(z);
}
```

Save the code and reload it in your browser, then try calling the `a()` and `b()` functions from the JavaScript console:

```js
a();
b();
```

You should see the `y` and `z` values output in the page. This works fine, as the `output()` function is being called inside the other functions — in the same scope as the variables it is printing are defined in, in each case. `output()` itself is available from anywhere, as it is defined in the global scope.
6. Now try updating your code like this:

```js
function a() {
  var y = 2;
  output(x);
}

function b() {
  var z = 3;
  output(x);
}
```

Save and reload again, and try this again in your JavaScript console:

```js
a();
b();
```

Both the `a()` and `b()` call should output the value of x — 1. These work fine because even though the `output()` calls are not in the same scope as `x` is defined in, `x` is a global variable so is available inside all code, everywhere.
7. Finally, try updating your code like this:

```js
function a() {
  var y = 2;
  output(z);
}

function b() {
  var z = 3;
  output(y);
}
```

Save and reload again, and try this again in your JavaScript console:

```js
a();
b();
```

This time the `a()` and `b()` calls will both return that annoying "ReferenceError: z is not defined" error — this is because the `output()` calls and the variables they are trying to print are not defined inside the same function scopes — the variables are effectively invisible to those function calls.

**備註：** The same scoping rules do not apply to loop (e.g. `for() { ... }`) and conditional blocks (e.g. `if() { ... }`) — they look very similar, but they are not the same thing! Take care not to get these confused.

**備註：** The ReferenceError: "x" is not defined error is one of the most common you'll encounter. If you get this error and you are sure that you have defined the variable in question, check what scope it is in.

### 函數裡的函數

Keep in mind that you can call a function from anywhere, even inside another function. This is often used as a way to keep code tidy — if you have a big complex function, it is easier to understand if you break it down into several sub-functions:

```js
function myBigFunction() {
  var myValue;

  subFunction1();
  subFunction2();
  subFunction3();
}

function subFunction1() {
  console.log(myValue);
}

function subFunction2() {
  console.log(myValue);
}

function subFunction3() {
  console.log(myValue);
}
```

Just make sure that the values being used inside the function are properly in scope. The example above would throw an error `ReferenceError: myValue is not defined`, because although the `myValue` variable is defined in the same scope as the function calls, it is not defined inside the function definitions — the actual code that is run when the functions are called. To make this work, you'd have to pass the value into the function as a parameter, like this:

```js
function myBigFunction() {
  var myValue = 1;

  subFunction1(myValue);
  subFunction2(myValue);
  subFunction3(myValue);
}

function subFunction1(value) {
  console.log(value);
}

function subFunction2(value) {
  console.log(value);
}

function subFunction3(value) {
  console.log(value);
}
```

結論
--

這篇文章探討了函數背後的基本觀念，為下一章的實戰演練以及帶你一步步建立你的自訂函數奠定基礎。

更多請看
----

* 函數詳細指南 — 包含一些本文為提及的進階功能
* 函數參考文件 (en-US)
* 預設參數、箭頭函數 — 進階概念參考文件
* 前頁
* Overview: Building blocks
* 次頁  (en-US)


循環代碼 - 學習該如何開發 Web

循環代碼
====

* 前頁
* Overview: Building blocks
* 次頁

編程語言對於快速完成重複性任務非常有用，從多個基本計算到幾乎任何其他需要完成大量類似工作的情況。 在這裡，我們將看看 JavaScript 中可用於處理此類需求的循環結構。

|  |  |
| --- | --- |
| Prerequisites: |
 Basic computer literacy, a basic understanding of HTML and CSS,
 JavaScript first steps.
  |
| Objective: | To understand how to use loops in JavaScript. |

保持循環
----

循環，循環，循環。 除了與受歡迎的早餐穀物，過山車和音樂作品有關聯，它們還是編程中的關鍵概念。 編程循環都是一遍又一遍地做同一件事-在編程方面被稱為迭代。

讓我們考慮一個農民的例子，他要確保他有足夠的食物來養家糊口。 他可能使用以下循環來實現此目的：

![](/zh-TW/docs/Learn/JavaScript/Building_blocks/Looping_code/loop_js-02-farm.png)

循環通常具有以下一項或多項功能：

* 一個用一定值初始化的計數器-這是循環的起點（上面的「開始：我沒有食物」）。
* 一種條件，它是對/錯測試，用於確定循環是繼續運行還是停止（通常在計數器達到某個值時）。 「我是否有足夠的食物？」對此進行了說明。 以上。 假設他需要 10 份食物來養家糊口的話。
* 一個迭代器，通常在每個連續循環上使計數器增加一小部分，直到條件不再成立為止。 上面我們沒有明確說明這一點，但是我們可以考慮一下農民每小時可以收集 2 份食物。 每小時之後，他收集的食物數量增加了 2，然後他檢查是否有足夠的食物。 如果他已達到 10 個部分（此時條件不再成立，則循環退出），他可以停止收集並回家。

In pseudocode (en-US), this would look something like the following:
```

loop(food = 0; foodNeeded = 10) {
  if (food = foodNeeded) {
    exit loop;
    // We have enough food; let's go home
  } else {
    food += 2; // Spend an hour collecting 2 more food
    // loop will then run again
  }
}
```

因此，所需的食物數量設置為 10，而農民當前擁有的食物數量設置為 0。在循環的每次迭代中，我們檢查農民擁有的食物數量是否等於他所需的數量。 如果是這樣，我們可以退出循環。 如果不是這樣，農民將花一個小時收集兩份食物，然後循環再次運行。

### 不用麻煩

在這一點上，你可能了解了循環背後的高級概念，但你可能在想：「好，很好，但這如何幫助我編寫更好的 JavaScript 代碼？」 如前所述，循環與一次又一次地執行同一操作有關，這對於快速完成重複性任務非常有用。

通常，代碼在每次循環的每次迭代中都會略有不同，這意味著你可以完成全部相似但略有不同的任務，一般情況，如果你要執行許多不同的計算，則需要不斷地執行不同的式子，而不能一遍又一遍重複！

讓我們看一個示例，以完美地說明為什麼循環是如此便利。 Let's say we wanted to draw 100 random circles on a `<canvas>` element (press the *Update* button to run the example again and again to see different random sets):
```

<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Random canvas circles</title>
    <style>
 html {
 width: 100%;
 height: inherit;
 background: #ddd;
 }

 canvas {
 display: block;
 }

 body {
 margin: 0;
 }

 button {
 position: absolute;
 top: 5px;
 left: 5px;
 }
 </style>
  </head>
  <body>
    <button>Update</button>

    <canvas></canvas>

    <script>
 var btn = document.querySelector("button");
 var canvas = document.querySelector("canvas");
 var ctx = canvas.getContext("2d");

 var WIDTH = document.documentElement.clientWidth;
 var HEIGHT = document.documentElement.clientHeight;

 canvas.width = WIDTH;
 canvas.height = HEIGHT;

 function random(number) {
 return Math.floor(Math.random() \* number);
 }

 function draw() {
 ctx.clearRect(0, 0, WIDTH, HEIGHT);
 for (var i = 0; i < 100; i++) {
 ctx.beginPath();
 ctx.fillStyle = "rgba(255,0,0,0.5)";
 ctx.arc(random(WIDTH), random(HEIGHT), random(50), 0, 2 \* Math.PI);
 ctx.fill();
 }
 }

 btn.addEventListener("click", draw);
 </script>
  </body>
</html>
```

你現在不必了解所有代碼，但讓我們看一下實際繪製 100 個圓圈的代碼部分：

```js
for (var i = 0; i < 100; i++) {
  ctx.beginPath();
  ctx.fillStyle = "rgba(255,0,0,0.5)";
  ctx.arc(random(WIDTH), random(HEIGHT), random(50), 0, 2 \* Math.PI);
  ctx.fill();
}
```

* `代碼前面定義的random（），返回0到x-1之間的整數。`
* `WIDTH和HEIGHT是內部瀏覽器窗口的寬度和高度。`

你應該了解基本概念-我們正在使用一個循環來運行此代碼的 100 次迭代，每個迭代在頁面上的隨機位置繪製一個圓圈。 無論我們繪製 100 個圓，1000 個還是 10,000 個，所需的代碼量都是相同的。 只需更改一個數字。

如果我們不在此處使用循環，則必須為每個要繪製的圓重複以下代碼：

```js
ctx.beginPath();
ctx.fillStyle = "rgba(255,0,0,0.5)";
ctx.arc(random(WIDTH), random(HEIGHT), random(50), 0, 2 \* Math.PI);
ctx.fill();
```

這將變得很無聊，並且很難很快維護。 循環確實是最好的。

循環的規範
-----

讓我們開始探索一些特定的循環結構。 第一個是 for 循環，你將在大多數時候使用它，它具有以下語法：

```js
for (initializer; exit - condition; final - expression) {
  // code to run
}
```

這裡我們有：

1. 關鍵字「 for」，即跟隨其後的一些括號。
2. 在括號內，我們有三個項目，以 ; 分隔：
    1. 初始化程序-通常是一個設置為數字的變量，該變量將遞增以計算循環運行的次數。 有時也稱為計數器變量。
    2. 退出條件-如前所述，它定義了循環何時應停止循環。 通常，這是一個具有比較運算符的表達式，該測試用於檢驗是否滿足退出條件。
    3. 最終表達式—每當循環經過完整的迭代時，總是對它進行評估（或運行）。 它通常用於遞增（或在某些情況下遞減）計數器變量，以使其更接近退出條件值。
3. 一些花括號包含一個代碼塊-每次循環迭代時都將運行此代碼。

讓我們看一個真實的例子，以便我們可以更清楚地看到它們的作用。

```js
var cats = ["Bill", "Jeff", "Pete", "Biggles", "Jasmin"];
var info = "My cats are called ";
var para = document.querySelector("p");

for (var i = 0; i < cats.length; i++) {
  info += cats[i] + ", ";
}

para.textContent = info;
```

這為我們提供了以下輸出：
```

<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Basic for loop example</title>
    <style></style>
  </head>
  <body>
    <p></p>

    <script>
 var cats = ["Bill", "Jeff", "Pete", "Biggles", "Jasmin"];
 var info = "My cats are called ";
 var para = document.querySelector("p");

 for (var i = 0; i < cats.length; i++) {
 info += cats[i] + ", ";
 }

 para.textContent = info;
 </script>
  </body>
</html>
```

**備註：** You can find this example code on GitHub too (also see it running live).

這顯示了一個循環，該循環用於遍歷數組中的項目並對其進行處理-這是 JavaScript 中非常常見的模式。 這裡：

1. 迭代器 i 從 0 開始（變量 i = 0）。
2. 它被告知運行，直到它不再小於 cats 數組的長度為止。 這很重要,退出條件顯示了循環仍將運行的條件。 因此，在這種情況下，儘管 i <cats.length 仍然為 true，循環仍將運行。
3. 在循環內部，我們將當前循環項（cats [i]是 cats [無論 i 當時是什麼]）與一個逗號和一個空格連接到 info 變量的末尾。 所以：
    1. 在第一次運行中，i = 0，因此 cats [0] +'，'將連接到 info（「 Bill，」）上。
    2. 在第二次運行中，i = 1，因此 cats [1] +'，'將連接到 info（「 Jeff，」）上
    3. 等等。每次循環運行後，將 1 加到 i（i ++），然後該過程將再次開始。
4. 當 i 等於 cats.length 時，循環將停止，瀏覽器將繼續循環下方的下一段代碼。

**備註：** We have made the exit condition `i < cats.length`, not `i <= cats.length`, because computers count from 0, not 1 — we are starting `i` at `0`, and going up to `i = 4` (the index of the last array item). `cats.length` returns 5, as there are 5 items in the array, but we don't want to get up to `i = 5`, as that would return `undefined` for the last item (there is no array item with an index of 5). So therefore we want to go up to 1 less than `cats.length` (`i <`), not the same as `cats.length` (`i <=`).

**備註：** A common mistake with exit conditions is making them use "equal to" (`===`) rather than say "less than or equal to" (`<=`). If we wanted to run our loop up to `i = 5`, the exit condition would need to be `i <= cats.length`. If we set it to `i === cats.length`, the loop would not run at all because `i` is not equal to `5` on the first loop iteration, so it would stop immediately.

我們剩下的一個小問題是最終輸出語句的格式不太正確：

>
> My cats are called Bill, Jeff, Pete, Biggles, Jasmin,
>
>
>

理想情況下，我們希望在最終循環迭代中更改串聯，以使句子的末尾沒有逗號。 好吧，沒問題-我們可以很高興地在 for 循環中插入一個條件來處理這種特殊情況：

```js
for (var i = 0; i < cats.length; i++) {
  if (i === cats.length - 1) {
    info += "and " + cats[i] + ".";
  } else {
    info += cats[i] + ", ";
  }
}
```

**備註：** You can find this example code on GitHub too (also see it running live).

**警告：** With for — as with all loops — you must make sure that the initializer is iterated so that it eventually reaches the exit condition. If not, the loop will go on forever, and either the browser will force it to stop, or it will crash. This is called an **infinite loop**.

中斷退出循環
------

如果要在所有迭代完成之前退出循環，可以使用 break 語句。 在查看 switch 語句時，我們已經在上一篇文章中遇到了這一問題—當在 switch 語句中遇到與輸入表達式匹配的 case 時，break 語句立即退出 switch 語句並移至其後的代碼上。

循環也是如此，-break 語句將立即退出循環，並使瀏覽器繼續執行緊隨其後的任何代碼。

假設我們要搜索一系列聯繫人和電話號碼，然後僅返回我們要查找的號碼？ 首先，提供一些簡單的 HTML-文本 `<input>` (en-US) 允許我們輸入要搜索的名稱，`<button>` (en-US) 元素以提交搜索，以及 `<p>` (en-US) 元素以在以下位置顯示結果：

```html
<label for="search">Search by contact name: </label>
<input id="search" type="text" />
<button>Search</button>

<p></p>
```

Now on to the JavaScript:

```js
var contacts = [
  "Chris:2232322",
  "Sarah:3453456",
  "Bill:7654322",
  "Mary:9998769",
  "Dianne:9384975",
];
var para = document.querySelector("p");
var input = document.querySelector("input");
var btn = document.querySelector("button");

btn.addEventListener("click", function () {
  var searchName = input.value;
  input.value = "";
  input.focus();
  for (var i = 0; i < contacts.length; i++) {
    var splitContact = contacts[i].split(":");
    if (splitContact[0] === searchName) {
      para.textContent =
        splitContact[0] + "'s number is " + splitContact[1] + ".";
      break;
    } else {
      para.textContent = "Contact not found.";
    }
  }
});
```

```
<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Simple contact search example</title>
    <style></style>
  </head>
  <body>
    <label for="search">Search by contact name: </label>
    <input id="search" type="text" />
    <button>Search</button>

    <p></p>

    <script>
 var contacts = [
 "Chris:2232322",
 "Sarah:3453456",
 "Bill:7654322",
 "Mary:9998769",
 "Dianne:9384975",
 ];
 var para = document.querySelector("p");
 var input = document.querySelector("input");
 var btn = document.querySelector("button");

 btn.addEventListener("click", function () {
 var searchName = input.value;
 input.value = "";
 input.focus();
 for (var i = 0; i < contacts.length; i++) {
 var splitContact = contacts[i].split(":");
 if (splitContact[0] === searchName) {
 para.textContent =
 splitContact[0] + "'s number is " + splitContact[1] + ".";
 break;
 } else if (i === contacts.length - 1)
 para.textContent = "Contact not found.";
 }
 });
 </script>
  </body>
</html>
```

1. 首先，我們有一些變量定義-我們有一個聯繫信息陣列，每個項目都是一個字符串，其中包含用冒號分隔的姓名和電話號碼。
2. 接下來，我們將事件監聽器附加到按鈕（btn），以便在按下按鈕時，將運行一些代碼來執行搜索並返回結果。
3. 我們將輸入到文本輸入中的值存儲在一個名為 searchName 的變量中，然後清空文本輸入並再次對其進行聚焦，以準備進行下一次搜索。
4. 現在到有趣的部分，for 循環：
    1. 我們從 0 開始啟動計數器，運行循環直到計數器不再小於 contact.length，然後在每次循環之後將 i 遞增 1。
    2. 在循環內部，我們首先將當前觸點（contacts [i]）分割為冒號字符，並將得到的兩個值存儲在名為 splitContact 的數組中。
    3. 然後，我們使用條件語句來測試 splitContact [0]（聯繫人的姓名）是否等於輸入的 searchName。 如果是這樣，我們在段落中輸入一個字符串以報告聯繫人的電話號碼，然後使用 break 結束循環。
5. 在（contacts.length-1）迭代之後，如果聯繫人姓名與輸入的搜索不匹配，則將段落文本設置為「找不到聯繫人。」，然後循環繼續進行迭代。

**備註：** You can view the full source code on GitHub too (also see it running live).

Skipping iterations with continue
---------------------------------

The continue statement works in a similar manner to `break`, but instead of breaking out of the loop entirely, it skips to the next iteration of the loop. Let's look at another example that takes a number as an input, and returns only the numbers that are squares of integers (whole numbers).

The HTML is basically the same as the last example — a simple text input, and a paragraph for output. The JavaScript is mostly the same too, although the loop itself is a bit different:

```js
var num = input.value;

for (var i = 1; i <= num; i++) {
  var sqRoot = Math.sqrt(i);
  if (Math.floor(sqRoot) !== sqRoot) {
    continue;
  }

  para.textContent += i + " ";
}
```

Here's the output:
```

<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Integer squares generator</title>
    <style></style>
  </head>
  <body>
    <label for="number">Enter number: </label>
    <input id="number" type="text" />
    <button>Generate integer squares</button>

    <p>Output:</p>

    <script>
 var para = document.querySelector("p");
 var input = document.querySelector("input");
 var btn = document.querySelector("button");

 btn.addEventListener("click", function () {
 para.textContent = "Output: ";
 var num = input.value;
 input.value = "";
 input.focus();
 for (var i = 1; i <= num; i++) {
 var sqRoot = Math.sqrt(i);
 if (Math.floor(sqRoot) !== sqRoot) {
 continue;
 }

 para.textContent += i + " ";
 }
 });
 </script>
  </body>
</html>
```

1. In this case, the input should be a number (`num`). The `for` loop is given a counter starting at 1 (as we are not interested in 0 in this case), an exit condition that says the loop will stop when the counter becomes bigger than the input `num`, and an iterator that adds 1 to the counter each time.
2. Inside the loop, we find the square root of each number using Math.sqrt(i) (en-US), then check whether the square root is an integer by testing whether it is the same as itself when it has been rounded down to the nearest integer (this is what Math.floor() does to the number it is passed).
3. If the square root and the rounded down square root do not equal one another (`!==`), it means that the square root is not an integer, so we are not interested in it. In such a case, we use the `continue` statement to skip on to the next loop iteration without recording the number anywhere.
4. If the square root IS an integer, we skip past the if block entirely so the `continue` statement is not executed; instead, we concatenate the current `i` value plus a space on to the end of the paragraph content.

**備註：** You can view the full source code on GitHub too (also see it running live).

while and do ... while
----------------------

`for` is not the only type of loop available in JavaScript. There are actually many others and, while you don't need to understand all of these now, it is worth having a look at the structure of a couple of others so that you can recognize the same features at work in a slightly different way.

First, let's have a look at the while loop. This loop's syntax looks like so:
```

initializer
while (exit-condition) {
  // code to run

  final-expression
}
```

This works in a very similar way to the for loop, except that the initializer variable is set before the loop, and the final-expression is included inside the loop after the code to run — rather than these two items being included inside the parentheses. The exit-condition is included inside the parentheses, which are preceded by the `while` keyword rather than `for`.

The same three items are still present, and they are still defined in the same order as they are in the for loop — this makes sense, as you still have to have an initializer defined before you can check whether it has reached the exit-condition; the final-condition is then run after the code inside the loop has run (an iteration has been completed), which will only happen if the exit-condition has still not been reached.

Let's have a look again at our cats list example, but rewritten to use a while loop:

```js
var i = 0;

while (i < cats.length) {
  if (i === cats.length - 1) {
    info += "and " + cats[i] + ".";
  } else {
    info += cats[i] + ", ";
  }

  i++;
}
```

**備註：** This still works just the same as expected — have a look at it running live on GitHub (also view the full source code).

The do...while loop is very similar, but provides a variation on the while structure:
```

initializer
do {
  // code to run

  final-expression
} while (exit-condition)
```

In this case, the initializer again comes first, before the loop starts. The `do` keyword directly precedes the curly braces containing the code to run and the final-expression.

The differentiator here is that the exit-condition comes after everything else, wrapped in parentheses and preceded by a `while` keyword. In a `do...while` loop, the code inside the curly braces is always run once before the check is made to see if it should be executed again (in while and for, the check comes first, so the code might never be executed).

Let's rewrite our cat listing example again to use a `do...while` loop:

```js
var i = 0;

do {
  if (i === cats.length - 1) {
    info += "and " + cats[i] + ".";
  } else {
    info += cats[i] + ", ";
  }

  i++;
} while (i < cats.length);
```

**備註：** Again, this works just the same as expected — have a look at it running live on GitHub (also view the full source code).

**警告：** With while and do...while — as with all loops — you must make sure that the initializer is iterated so that it eventually reaches the exit condition. If not, the loop will go on forever, and either the browser will force it to stop, or it will crash. This is called an **infinite loop**.

Active learning: Launch countdown
---------------------------------

In this exercise, we want you to print out a simple launch countdown to the output box, from 10 down to Blast off. Specifically, we want you to:

* Loop from 10 down to 0. We've provided you with an initializer — `var i = 10;`.
* For each iteration, create a new paragraph and append it to the output `<div>`, which we've selected using `var output = document.querySelector('.output');`. In comments, we've provided you with three code lines that need to be used somewhere inside the loop:
    + `var para = document.createElement('p');` — creates a new paragraph.
    + `output.appendChild(para);` — appends the paragraph to the output `<div>`.
    + `para.textContent =` — makes the text inside the paragraph equal to whatever you put on the right hand side, after the equals sign.
* Different iteration numbers require different text to be put in the paragraph for that iteration (you'll need a conditional statement and multiple `para.textContent =` lines):
    + If the number is 10, print "Countdown 10" to the paragraph.
    + If the number is 0, print "Blast off!" to the paragraph.
    + For any other number, print just the number to the paragraph.
* Remember to include an iterator! However, in this example we are counting down after each iteration, not up, so you **don't** want `i++` — how do you iterate downwards?

If you make a mistake, you can always reset the example with the "Reset" button. If you get really stuck, press "Show solution" to see a solution.
```

<h2>Live output</h2>
<div class="output" style="height: 410px;overflow: auto;"></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: 300px;width: 95%">
var output = document.querySelector('.output');
output.innerHTML = '';

// var i = 10;

// var para = document.createElement('p');
// para.textContent = ;
// output.appendChild(para);
</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 output = document.querySelector('.output');\noutput.innerHTML = '';\n\nvar i = 10;\n\nwhile(i >= 0) {\n var para = document.createElement('p');\n if(i === 10) {\n para.textContent = 'Countdown ' + i;\n } else if(i === 0) {\n para.textContent = 'Blast off!';\n } else {\n para.textContent = i;\n }\n\n output.appendChild(para);\n\n i--;\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();
};
```

Active learning: Filling in a guest list
----------------------------------------

In this exercise, we want you to take a list of names stored in an array, and put them into a guest list. But it's not quite that easy — we don't want to let Phil and Lola in because they are greedy and rude, and always eat all the food! We have two lists, one for guests to admit, and one for guests to refuse.

Specifically, we want you to:

* Write a loop that will iterate from 0 to the length of the `people` array. You'll need to start with an initializer of `var i = 0;`, but what exit condition do you need?
* During each loop iteration, check if the current array item is equal to "Phil" or "Lola" using a conditional statement:
    + If it is, concatenate the array item to the end of the `refused` paragraph's `textContent`, followed by a comma and a space.
    + If it isn't, concatenate the array item to the end of the `admitted` paragraph's `textContent`, followed by a comma and a space.

We've already provided you with:

* `var i = 0;` — Your initializer.
* `refused.textContent +=` — the beginnings of a line that will concatenate something on to the end of `refused.textContent`.
* `admitted.textContent +=` — the beginnings of a line that will concatenate something on to the end of `admitted.textContent`.

Extra bonus question — after completing the above tasks successfully, you will be left with two lists of names, separated by commas, but they will be untidy — there will be a comma at the end of each one. Can you work out how to write lines that slice the last comma off in each case, and add a full stop to the end? Have a look at the Useful string methods article for help.

If you make a mistake, you can always reset the example with the "Reset" button. If you get really stuck, press "Show solution" to see a solution.
```

<h2>Live output</h2>
<div class="output" style="height: 100px;overflow: auto;">
  <p class="admitted">Admit:</p>
  <p class="refused">Refuse:</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: 400px;width: 95%">
var people = ['Chris', 'Anne', 'Colin', 'Terri', 'Phil', 'Lola', 'Sam', 'Kay', 'Bruce'];

var admitted = document.querySelector('.admitted');
var refused = document.querySelector('.refused');
admitted.textContent = 'Admit: ';
refused.textContent = 'Refuse: '

// var i = 0;

// refused.textContent += ;
// admitted.textContent += ;

</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 people = ['Chris', 'Anne', 'Colin', 'Terri', 'Phil', 'Lola', 'Sam', 'Kay', 'Bruce'];\n\nvar admitted = document.querySelector('.admitted');\nvar refused = document.querySelector('.refused');\n\nadmitted.textContent = 'Admit: ';\nrefused.textContent = 'Refuse: '\nvar i = 0;\n\ndo {\n if(people[i] === 'Phil' || people[i] === 'Lola') {\n refused.textContent += people[i] + ', ';\n } else {\n admitted.textContent += people[i] + ', ';\n }\n i++;\n} while(i < people.length);\n\nrefused.textContent = refused.textContent.slice(0,refused.textContent.length-2) + '.';\nadmitted.textContent = admitted.textContent.slice(0,admitted.textContent.length-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();
};
```

Which loop type should you use?
-------------------------------

For basic uses, `for`, `while`, and `do...while` loops are largely interchangeable. They can all be used to solve the same problems, and which one you use will largely depend on your personal preference — which one you find easiest to remember or most intuitive. Let's have a look at them again.

First `for`:
```

for (initializer; exit-condition; final-expression) {
  // code to run
}
```

`while`:
```

initializer
while (exit-condition) {
  // code to run

  final-expression
}
```

and finally `do...while`:
```

initializer
do {
  // code to run

  final-expression
} while (exit-condition)
```

We would recommend `for`, at least to begin with, as it is probably the easiest for remembering everything — the initializer, exit-condition, and final-expression all have to go neatly into the parentheses, so it is easy to see where they are and check that you aren't missing them.

**備註：** There are other loop types/features too, which are useful in advanced/specialized situations and beyond the scope of this article. If you want to go further with your loop learning, read our advanced Loops and iteration guide.

Conclusion
----------

This article has revealed to you the basic concepts behind, and different options available when, looping code in JavaScript. You should now be clear on why loops are a good mechanism for dealing with repetitive code, and be raring to use them in your own examples!

If there is anything you didn't understand, feel free to read through the article again, or contact us to ask for help.

See also
--------

* Loops and iteration in detail
* for statement reference
* while and do...while references
* break and continue references
* What's the Best Way to Write a JavaScript For Loop? — some advanced loop best practices
* 前頁
* Overview: Building blocks
* 次頁


非同步的 JavaScript 介紹 - 學習該如何開發 Web

非同步的 JavaScript 介紹
==================

* 前頁
* Overview: Asynchronous
* 次頁

在本篇文章中我們會先簡短回顧在同步的 JavaScript 中所遭遇到的問題，並預先看看往後將會使用哪些非同步的 JavaScript 技巧來解決此問題。

|  |  |
| --- | --- |
| 先備知識： | 基本的電腦概念，理解 JavaScript 的基本原理。 |
| 學習目標： |
 熟悉什麼是非同步的 Javascript ，與同步的 Javascript
 有甚麼差異，以及有哪些使用案例。
  |

同步的 JavaScript
--------------

在我們了解什麼是非同步（**asynchronous** ） JavaScript 之前，我們應該確保我們已經了解什麼是同步（ **synchronous** ） JavaScript 。在本章節會回顧我們在前篇文章所看到的一些資訊。

我們在前一個學習單元所看到的大部分功能都是同步的——當跑一段程式碼，瀏覽器會將執行結果盡快的回傳。我們來看看一則簡單的範例（線上範例，原始碼）：

```js
const btn = document.querySelector("button");
btn.addEventListener("click", () => {
  alert("你點擊了我！");

  let pElem = document.createElement("p");
  pElem.textContent = "這是一段新加的文字。";
  document.body.appendChild(pElem);
});
```

在這個程式區塊，指令是一行接著一行的執行：

1. 我們先取得已經在 DOM 裡面的 `<button>` (en-US) 元素的參考。
2. 我們加入一個 `click` (en-US) 事件監聽器，因此當按鈕被點擊時：
    1. 彈出 `alert()` (en-US) 訊息。
    2. 當關閉警告訊息後，我們新增一個 `<p>` (en-US) 元素。
    3. 給予一段文字內容。
    4. 最後，我們將文字段落嵌在文件本體（ document body ）上。

當正在執行每一個操作時，什麼事情都不會發生——渲染暫時停止。我們在上一篇文章有提到，這是因為 Javascript 是跑在一條單執行緒。主執行緒在同一時間只能做一件事情，直到目前操作完成為止其他的操作都會暫停。

所以在上面的範例中，直到警告視窗的確認按鈕被按下為止之前，文字段落都不會出現。你可以試試看底下的範例：
```

<button>點擊我</button>
```

**備註：** 使用 `alert()` (en-US) 有個很重要的事情要記得，雖然用在證明是否為同步的阻塞操作非常有用，但非常不建議用在實際的應用程式上，使用體驗將會相當糟糕。

非同步 JavaScript
--------------

如同先前所描述的原因（例如：和阻塞有關），現在許多 Web API 功能都是使用非同步程式碼的做法，特別是那些要從外部裝置存取或抓取某些種類資源的功能，像是從網路抓取檔案、存取資料庫並回傳資料、從網路攝影機取得視訊串流或者是將畫面透過廣播的方式顯示到 VR 頭盔等。

為什麼同步的程式碼很難做到這些事？我們快速地看個例子。當你想要從伺服器抓取一張影像，你是沒有辦法馬上獲得結果的。這個意思是下面的程式碼（虛擬碼）是行不通的：

```js
let response = fetch("myImage.png"); // fetch is asynchronous
let blob = response.blob();
// display your image blob in the UI somehow
```

那是因為你不知道下載影像需要花上多少時間，所以當執行第二行時它將拋出一段錯誤（也許間歇性的發生，也許每一次都發生）這是因為 `response` 尚未取得結果還不能使用。因此在它嘗試做接下來的任何事之前，你需要讓你的程式碼先等到 `response` 回傳結果。

你將會在 Javascript 程式碼碰到兩種主要類型的非同步程式碼風格，舊式的回呼風格（callback）以及新式的 promise 風格。在接下來的章節我們將會依次的檢視它們。

非同步回呼
-----

呼叫一個將在背景開始執行程式碼的函式，非同步回呼是作為該呼叫函式的引數（ argument ）的函式。當在背景環境執行的程式碼完成時，就會呼叫回呼函式（ callback function ）讓你知道工作已經結束或者是一些有趣的事情已經發生了。現在回呼似乎有點過時了，但你依然會在一些較舊但仍常被使用的 API 發現到這樣的寫法。

一個非同步回呼的例子就是作為在 `addEventListener()` 的第二個參數（正如我們在之前的例子看到的）：

```js
btn.addEventListener("click", () => {
  alert("你點擊了我！");

  let pElem = document.createElement("p");
  pElem.textContent = "這是一段新加的文字。";
  document.body.appendChild(pElem);
});
```

第一個參數是要被監聽的事件類型，第二個參數是當事件被觸發時會呼叫的回呼函式。

當我們將回呼函式做為一個引數給其他函式時，我們只傳送該回呼函式的參考做為引數，換句話說，回呼函式**並非**立即被執行。它將會在呼叫函式的內部某個地方非同步的「回去呼叫」它（因此稱回呼）。呼叫函式有責任的在適當的時機去執行該回呼函式。

自己寫一個包含回呼的呼叫函式很容易。我們來看看另一個從 `XMLHttpRequest` API 載入資源的例子（線上範例，原始碼）：

```js
function loadAsset(url, type, callback) {
  let xhr = new XMLHttpRequest();
  xhr.open("GET", url);
  xhr.responseType = type;

  xhr.onload = function () {
    callback(xhr.response);
  };

  xhr.send();
}

function displayImage(blob) {
  let objectURL = URL.createObjectURL(blob);

  let image = document.createElement("img");
  image.src = objectURL;
  document.body.appendChild(image);
}

loadAsset("coffee.jpg", "blob", displayImage);
```

在這裡我們新增一個 `displayImage()` 函式，將 blob 做為一個參數傳進去函式來產生物件網址，然後建立一個可以用網址來顯示的影像，並將這個網址附加在 document 的 `<body>` 標籤內。然而，我們再新增一個 `loadAsset()` 函式，將回呼作為參數並伴隨抓取資源的網址以及內容型式傳進去函式。它使用 `XMLHttpRequest` （通常縮寫成「 XHR 」）根據網址去抓取資源，並將回傳結果傳送到回呼去做一些事情。在這個例子回呼正在等待 XHR 完成下載資源（使用 `onload` (en-US) 事件處理器）後，再將資源傳遞給回呼。

回呼是多樣性的——它不只可以讓你控制執行函式的呼叫順序以及在不同函式間傳遞的參數，還可以讓你根據情況將資料傳遞到不同的函式。所以你可以根據下載的回傳結果採取不同的處理方式，例如 `processJSON()` ， `displayText()` 等等。

注意不是所有的回呼都是非同步的——有些是跑在同步上。有個例子是當我們使用 `Array.prototype.forEach()` (en-US) 在陣列裡面用迴圈來遍歷每一個項目（線上範例，原始碼）：

```js
const gods = ["Apollo", "Artemis", "Ares", "Zeus"];

gods.forEach(function (eachName, index) {
  console.log(index + ". " + eachName);
});
```

在這個例子我們用迴圈遍歷一個含有希臘諸神名稱的陣列並在控制台印出每一個索引和值。 `forEach()` 的預期參數是一個回呼函式，它有兩個參數代表陣列每個項目的名稱和索引。然而，它並未等待任何事情——它是立即的執行。

Promise
-------

Promise 是作為非同步程式碼的一種新風格樣式，你將會在現代 Web API 看到這種用法。一個好例子是 `fetch()` (en-US) API ，它基本上就像更新穎、更有效率版本的 `XMLHttpRequest` 。我們藉由從伺服器提取資料這一篇文章快速地來看一個例子吧：

```js
fetch("products.json")
  .then(function (response) {
    return response.json();
  })
  .then(function (json) {
    let products = json;
    initialize(products);
  })
  .catch(function (err) {
    console.log("Fetch problem: " + err.message);
  });
```

**備註：** 你可以在 GitHub （原始碼，線上範例）找到最終版本。

在這裡我們看到 `fetch()` 帶一個參數——你想要在網路上提取資源的網址——並回傳一個 promise 。這一個 promise 用來表示一個完成或失敗的非同步操作的物件。它代表的是一種中間的狀態。本質上，它代表瀏覽器述說著：「我承諾我會盡快給予你一個答覆」，因此名稱就叫做「 promise 」。

我們可以透過練習來習慣這個概念；在動作上它感覺有點像是 薛丁格的貓。任何可能的結果已經發生，所以 fetch 指令正在等待瀏覽器在未來的某個時間點完成操作後並回傳的結果。我們接著在 `fetch()` 的結束會鏈結三個程式區塊：

* 兩個 `then()` 區塊。兩個都含有回呼函式且先前的操作成功時就會執行，每一個回呼函式都會收到上一個成功完成操作的結果，因此你可以繼續執行一些事情。每一個 `.then()` 區塊都會回傳另一個 promise ，代表你可以將多個 `.then()` 區塊彼此作連結，所以多個非同步操作可以一個接著一個被用來依序執行。
* 如果任何一個 `.then()` 區塊失敗就會跑到 `catch()` 區塊——類似像在同步區塊內部的 `try...catch` 做法，在 `catch()` 內部會提供一個失敗的物件，可以用來報告是發生甚麼類型的錯誤。要注意到同步的 `try...catch` 不能與 promises 一起做使用，儘管它可以和 async ／ await (en-US) 待配使用，這稍後將會學習到。

**備註：** 你將會在稍後的單元學習到更多關於 promise 的觀念，即使現在尚未完全理解你也不需要太擔心。

### 事件佇列

像 promise 的非同步的操作被放進**事件佇列**中，佇列內的任務會在主執行緒完成它自己的工作後執行，所以它**並不會阻擋**後續 Javascript 程式碼的執行。在佇列內的操作會盡快完成並將結果回傳給 Javascript 環境。

### Promise 對上回呼

Promise 和舊式的回呼有一些相似點。他們本質上都是回傳一個物件到回呼函式，而並非必須將回呼傳遞到呼叫函式。

然而 promise 更是專門設計用來處理非同步的操作，比起舊式的回呼有更多的優點：

* 你可以使用 `.then()` 來鏈結多的非同步操作，將一個操作的結果作為下一個操作的輸入。這個對經常會導致「死亡金字塔」（ pyramid of doom ，通常也被稱作為 回呼地獄）的回呼來說相當難辦到。
* Promise 的執行順序總是依照放入事件佇列的順序來嚴格執行。
* 更佳的錯誤處理機制——所有的錯誤都在最後的 `.catch()` 區塊來統一處理，而不是在個別的區塊做處理。
* Promise 避免失去控制權，不像舊式的回呼那樣傳送給第三方程式庫後就會無法控制回呼函式怎麼被執行。
非同步程式碼的天性
---------

我們來探索一個可以更進一步說明非同步程式碼天性的範例，並說明當我們無法充分了解程式碼的執行順序以及嘗試將非同步程式碼當作同步程式碼會發生什麼事。底下的範例和我們之前看到的相當相似（ 線上範例，原始碼）。區別在於我們加入一些 `console.log()` (en-US) 來說明程式碼是否會依照你以為的順序來執行。

```js
console.log("Starting");
let image;

fetch("coffee.jpg")
  .then((response) => {
    console.log("It worked :)");
    return response.blob();
  })
  .then((myBlob) => {
    let objectURL = URL.createObjectURL(myBlob);
    image = document.createElement("img");
    image.src = objectURL;
    document.body.appendChild(image);
  })
  .catch((error) => {
    console.log(
      "There has been a problem with your fetch operation: " + error.message,
    );
  });

console.log("All done!");
```

瀏覽器將開始執行程式碼，看到第一行顯示 `Starting` 的 `console.log()` 並執行它，接著新增一個 `image` 變數。

接著它會移動至下一行並開始執行 `fetch()` 區塊，但是因為執行 `fetch()` 是非同步的且不會阻塞，所以程式碼會跳過 promise 相關的程式碼後繼續執行，因此到達最後`All done!` 的 `console.log()` 並顯示在控制台上。

只有在完成 `fetch()` 並將回傳結果傳送至 `.then()` 之後，我們才會看到第二個 `console.log()` 顯示 `It worked :)` 的訊息出現。所以訊息的出現順序也許與你所期待的順序不同：

* Starting
* All done!
* It worked :)

如果上面的例子讓你感到混淆，那麼考慮一下下面更簡單的例子：

```js
console.log("registering click handler");

button.addEventListener("click", () => {
  console.log("get click");
});

console.log("all done");
```

這是一個非常相似的行為——第一個和第三個 `console.log()` 的訊息將會立即的顯示，但是第二個正在被阻塞直到有人點擊了滑鼠按鈕。和上一個例子運作的方式類似，除了之前的例子在第二步是因為要等待取得資源之後並顯示在螢幕上才會被阻塞住，而不是等待被點擊。

在這個不直觀的例子中，這樣的設定方式可能會導致問題的發生——你不能在同步的程式碼去依賴一個非同步程式碼的執行結果，你不能保證非同步的函式會在瀏覽器執行下一個同步程式碼之前回傳結果。

為了看這個動作，試著在本地複製我們的程式碼，並將最後的 `console.log()` 的呼叫改成底下的方式：

```js
console.log("All done! " + image.src + "displayed.");
```

你應該會在控制台收到一個錯誤的訊息：
```

TypeError: image is undefined; can't access its "src" property
```

這是因為在這個時間點瀏覽器試著去執行最後的 `console.log()` 時 `fetch()` 還沒有完成執行，所以 `image` 變數尚未賦予值因而導致錯誤。

**備註：** 由於安全性考量，你沒辦法呼叫 `fetch()` 從你的本地檔案系統抓取資料（或者其他在本地的相關操作）如果要在本地執行上面的範例，你需要在本地架起一個網路伺服器 (en-US)來執行。

主動學習：讓一切非同步化！
-------------

為了修復 `fetch()` 這個有問題的範例讓三個 `console.log()` 的敘述可以依序被執行你可以讓第三個 `console.log()` 也非同步的被執行。你可以新增一個新的 `.then()` 再將其放在裡面，這個新增的 `.then()` 再串連 promise 區塊的倒數第二個結尾；或者也可以將其放入原本的 `.then()` 中來解決這個問題。試著現在修復這個問題吧。

**備註：** 如果你的想法卡住，你可以在這裡找到解答（線上範例）。你也可以在我們稍後的優雅的使用 Promise 來處理非同步操作 (en-US) 這個單元找到更多關於 promise 的資訊。

結論
--

Javascript 基本上是一個同步性的、阻塞的，且是跑在單一執行緒的程式語言，也就是在同一時間只能執行一個操作。但是瀏覽器所定義的函式和 API 允許我們註冊一個不該被同步執行的函式，且這個函式應該在某些事件發生時需要非同步的被呼叫（到達指定的時間、使用者透過滑鼠互動，或者取得透過從網路所取到的資料）。這代表你可以讓你的程式碼在同時間做一些事情而不需暫停或阻塞你的主執行緒。

我們要讓程式碼同步地或非同步地執行取決於我們要做甚麼。

有時候我們希望事情能夠立即的被載入並發生。例如某些使用者定義的樣式，你希望網頁能夠盡快地使用那些樣式。

如果我們正在執行一些需要花一點時間的操作，就好比查詢資料庫並將結果填充到模板上，最好的方式是不要在主執行緒上執行並用非同步的方法來完成。隨著時間的學習，你將會了解到在更多的情況下，選擇使用非同步的技巧會比選擇同步的方式來的更合理。

* 前頁
* Overview: Asynchronous
* 次頁


JavaScript 是什麼? - 學習該如何開發 Web

JavaScript 是什麼?
===============

* Overview: First steps
* 次頁

歡迎來到 MDN 的 JavaScript 初學者課程！我們將在這個章節綜觀 JavaScript ，回答一些像是「它什麼？」和「可以使用它作什麼？」之的問題，並確保你了解 JavaScript 的特性。

|  |  |
| --- | --- |
| 先備條件： | 基本的電腦知識，基本了解 HTML 和 CSS 技術。 |
| 學習目標： | 了解 JavaScript 的本質、功能、以及它如何構成網站的一部分 |

高層次的定義
------

JavaScript 是一種腳本，也能稱它為程式語言，可以讓你在網頁中實現出複雜的功能。當網頁不只呈現靜態的內容，另外提供了像是：內容即時更新、地圖交動、繪製 2D/3D 圖形，影片播放控制……等，你就可以大膽地認為 JavaScript 已經參與其中。它是標準網頁技術蛋糕的第三層，而其他兩層（HTML 和 CSS）我們在其他學習單元中有更多詳細的介紹。

![](/zh-TW/docs/Learn/JavaScript/First_steps/What_is_JavaScript/cake.png)

* HTML 是一種標記語言，我們使用它組織網頁裡的內容並給予定義， 例如：定義段落、標題、資料表格，或是在頁面嵌入圖片和影片。.
* CSS 是一種樣式規則的語言，用來幫我們的 HTML 內容上套用樣式，例如：設置背景顏色、字型，或讓內容以多欄的方式呈現。
* JavaScript 是一種腳本語言，它使你能夠動態的更新內容、控制多媒體、動畫……幾乎所有事。（好吧，不是所有事情，但神奇的是你可以通過短短幾行 JavaScript 程式碼實現。）

這三層很好的構建在一起。讓我們以一個簡單的文字為例。我們可以使用 HTML 標記來表示它的結構和意圖：

```html
<p>Player 1: Chris</p>
```

![](/zh-TW/docs/Learn/JavaScript/First_steps/What_is_JavaScript/just-html.png)

然後我們可以加一些 CSS ，讓它看起來更好：

```css
p {
  font-family: "helvetica neue", helvetica, sans-serif;
  letter-spacing: 1px;
  text-transform: uppercase;
  text-align: center;
  border: 2px solid rgba(0, 0, 200, 0.6);
  background: rgba(0, 0, 200, 0.3);
  color: rgba(0, 0, 200, 0.6);
  box-shadow: 1px 1px 2px rgba(0, 0, 200, 0.4);
  border-radius: 10px;
  padding: 3px 10px;
  display: inline-block;
  cursor: pointer;
}
```

![](/zh-TW/docs/Learn/JavaScript/First_steps/What_is_JavaScript/html-and-css.png)

最後，我們可以加一些 JavaScript 來作出互動的行為：

```js
const para = document.querySelector("p");

para.addEventListener("click", updateName);

function updateName() {
  let name = prompt("輸入新的名字");
  para.textContent = "Player 1: " + name;
}
```

試試點擊這最後版本的文字，看看會發生什麼事情（你同樣也可以在 GitHub 找到這個範例，來查看原始碼或在線上執行）！

JavaScript 能做到更多，讓我們更深入地探索。

它究竟可以做什麼呢?
----------

JavaScript 語言的核心包含了很多常用的程式功能供你使用，如：

* 將有用的值存儲在變數中。例如上述例子，我們要求輸入一個新名字，然後將該名字存在名為`name`的變數裡。
* 對文本片段的操作（在程式裡稱作"字串"）。在上述例子中，我們拿了字串 "Player 1: " 並將其與 `name` 變數連接來創造完整文本標籤，如：''Player 1: Chris"。
* 執行程式碼，回應網頁上發生的某些事件。在上述例子中，我們使用 `click` (en-US) 事件，當按鈕被點擊時，便執行更新文本標籤的程式碼。
* 以及其他更多更多的功能！

然而，更令人興奮的是那些基於用戶端的 JavaScript 語言構建的功能。也就是所謂的 **應用程式介面**（**API**），提供 JavaScript 程式額外的超能力。

API 是預先製作完成的程式模組，支援開發者實現困難或無法實現的功能。在程式設計中就如同在建築房子的時候使用系統傢俱，拿預先裁好的板子用螺絲鎖上來組合成書架，相比從頭自己設計，找合適木材，切成正確的形狀和尺寸，再找到合適的螺絲最後組裝書架而言更簡單。

他們通常分為兩類。

![](/zh-TW/docs/Learn/JavaScript/First_steps/What_is_JavaScript/browser.png)

**瀏覽器 API**（Browser API）內建在你的瀏覽器中，能夠依本地的電腦環境輸出資料或實現複雜的功能。舉例而言：

* `DOM （文件物件模型） API` 讓你能操作 HTML 和 CSS，像是建立、移除或改變 HTML 元素，或動態地將新樣式套用到頁面…等等。每當你看到彈出視窗，或有新的內容出現在畫面上（就像上面的範例所展示的），那就是 DOM 在動作。
* `Geolocations（地理位置） API` 可以取得位置資訊。這就是 Google Maps 取得你的位置並標示在地圖上所透過的方式。
* `Canvas` 和 `WebGL` (en-US) API 可以讓你在網頁創造 2D 動畫及 3D 圖像。人們正使用這些技術來作一些令人驚奇的事，參見 Chrome Experiments 及 webglsamples.
* Audio 和 Video API (en-US) 像 `HTMLMediaElement` (en-US) 和 `WebRTC` (en-US) 一樣讓你可以使用多媒體做真正有趣的事情，例如在網頁中播放音樂或影片，或由網路攝影機頡取你的影像顯示在另一個人電腦裡（試試我們的簡單例子來了解）。

**備註：** 上面的許多範例無法在舊版的瀏覽器上運作。使用現代的瀏覽器像是 Firefox、Chrome、Edge 或 Opera 來嘗試執行你的程式總是比較好的。當你接近要交付作為產品的程式（也就是實際的用戶將要使用的時候)，就需要思考關於跨瀏覽器測試的事情。

**第三方 API** 預設不內建在瀏覽器裡，你通常由網路上取得他們的程式碼與資訊。例如：

* Twitter API 能讓你做很多事，像是顯示最新的 Twitter 貼文在你的網站上。
* Google Maps API 能讓你在自己的網站中嵌入訂製的地圖或其他相關功能。

**備註：** 我們不會在此涵蓋這些進階的 APIs。你可以在我們的 客戶端網頁 API 單元找到更多資訊。

那裡也有很多的東西。然而不要一頭熱陷進去。你不會在學習 JavaScript 24 小時後，就能開發出下一個 Facebook、Google 地圖或 Instagram 之類的產品出來。有許多的基礎的東西得先了解，這也是你在這裡的原因，讓我們繼續吧！

JavaScript 在你的頁面做了些什麼？
----------------------

這裡我們開始看一些程式碼，探索當 JavaScript 在你的頁面上執行時，發生了哪些事情。

簡單回顧一下當瀏覽器載入一個網站時會發生的事情（第一次是在我們的CSS 如何工作章節中提到）。當瀏覽器載入一個網頁，就是在執行環境（瀏覽器分頁）中執行程式碼（HTML，CSS 和 JavaScript）。就像是工廠收集原料（程式碼）並且產出商品（網頁呈現的結果）。

![](/zh-TW/docs/Learn/JavaScript/First_steps/What_is_JavaScript/execution.png)

透過 DOM API （上面提到的）動態調整 HTML 與 CSS 進行改變網頁呈現，在 JavaScript 是很常見的使用方式。要注意的是，檔案中的程式碼通常會以出現在頁面上的順序來執行。如果 JavaScript 比準備操作的 HTML 、 CSS 更早被載入，就可能會發生錯誤。你將會在這個章節的後段學到一些解決問題的方法，它在腳本載入策略的部分。

### 瀏覽器安全性

瀏覽器的每個分頁有獨立的空間來執行程式碼（技術上稱「執行環境 excution environments」），這表示在絕大部分的情形之下，每個分頁的程式碼是獨立運行的，不能直接影響其它分頁（或網站）裡的程式。這是一個好的安全措施，少了它，有心人會開始寫程式來偷取網站的資料，或是作其它不好的事情。

**備註：** 是有一些安全的方式能在不同的網頁、分頁之間傳遞程式和資料，不過這些進階的技術不會在涵蓋在這個單元中。

### JavaScript 的執行順序

當瀏覽器遇到一段 JavaScript 程式碼，通常會由上到下執行。這意味著你需要注意東西擺放的順序。為了說明，讓我們回到我們曾看過的第一個範例：

```js
const para = document.querySelector("p");

para.addEventListener("click", updateName);

function updateName() {
  let name = prompt("輸入新的名字");
  para.textContent = "Player 1: " + name;
}
```

這裡我們選擇了一個文字段落（第 1 行），然後加上事件偵聽器，所以當段落被點擊的時候，`updateName()` 程式區塊（5 到 8 行 ）會被執行。`updateName()` 程式區塊（這種可以重複使用的程式區塊被稱為「函數 function」）會向使用者要一個新的名字，然後將插到段落中，更新顯示的內容。

如果你交換前兩行的程式碼，它將不再正常運作。取而代之的，瀏覽器開發主控台 (en-US)會回報一個錯誤訊息：「`TypeError: para is undefined`」，意思是 `para` 物件尚不存在，所以我們在它上頭增加事件偵聽器。

**備註：** 這是一個很常見的錯誤，在你嘗試對物件進行操作之前，你需要注意它們已經存在。

### 直譯式與編譯式程式語言

你可能在程式相關的文章中看過**直譯式** (**interpreted** )與\*\*編譯式 (compiled)\*\*這兩個詞。在直譯式程式語言中，程式碼由上到下執行，而且執行的結果是立即回應得到的。在瀏覽器執行前，你不需要將程式轉換為其它的形式。程式碼的內容是以對程式人員友善的形式並直接能夠運作。

另一方面，編譯式程式語言需要在電腦執行之前轉換（編譯）成另一種形式。例如： C/C++ 在被電腦執行之前要編譯為組合語言。被執行的程式是一種二進位的格式，由程式原始碼產生出來。

JavaScript 是一個輕量的直接程式語言。網頁瀏覽器收到文字格式的 JavaScript 程式碼，並直接執行。以技術的角度來看，大多數現代的 JavaScript 直譯器實際上會使用一種稱為\*\*即時編譯（just-in-time compiling）\*\*的技術來提升執行表現。 JavaScript 被使用時，原始程式會被編譯成更快的二進位格式，讓它們能更有效率的運行。然而， JavaScript 仍然被認為是一種直譯式的程式語言，因為編譯動作是在程式運作時，而不是事先完成。

兩種語言都有各自的優點，但是我們不在此時討論這個議題。

### 伺服器端與用戶端程式

你也有可能聽過**伺服器端**與**客戶端**程式，尤其在網站開發的領域。客戶端程式在使用者的電腦中運作，當瀏覽網頁的時候，頁面中的客戶端程式被下載，接著被瀏覽器執行與顯示結果。在這個單元中，我們只談論**客戶端 JavaScript**。

另一方面，伺服器端的程式在伺服器上執行，接著產出的結果被瀏覽器下載後顯示。受歡迎的伺服器端網頁語言，包括 PHP、Python、Ruby 和 ASP.NET 以及… JavaScript！JavaScript 也能夠作為伺服器端程式語言，流行的 Node.js 環境就是一例。你可以在我們的動態網站—伺服器端網站程式設計主題中找到更多資訊。

### 動態與靜態程式

**動態**一詞被用於描述用戶端 JavaScript 和伺服器端程的式語言，用來描述具有在不同的狀況下更新網頁／網頁應用程式來顯示不同東西，依需要來產生新內容的能力。是根據要求生成新內容的能力。伺服器端程式在伺服器上動態產生的新內容，可能是來自資料庫中取出的數據，而用戶端 JavaScript 在收到由伺服器端要回來的資料，在瀏覽器內動態產生新的內容（如 HTML 表格）後加入頁面中呈現出來。在兩個情境中詞義稍微不同，但是相關的，兩種方法（伺服器端和用戶端）通常可以協同工作。

一個沒有動態更新內容能力的網頁被稱為**靜態**，它在任何時候都只顯示一樣的內容。

如何在網頁中增加 JavaScript？
--------------------

在 HTML 頁面中使用 JavaScript 與 CSS 的方法類似。在 HTML 中 CSS 藉著`<link>` (en-US) 元素引入外部樣式（stylesheets）以及 `<style>` (en-US) 元素定義內部樣式。JavaScript 在 HTML 中只需要一個朋友 — `<script>` 元素。讓我們了解它是如何運作。

### 內部的 JavaScript

1. 首先，下載一份 apply-javascript.html 範例儲存在自己的電腦上合適的目錄裡。
2. 用瀏覽器與文字編輯器打開範例。你會看到 HTML 建立了一個簡單的網頁，包含一個可點擊的按鈕。
3. 接著，切換到文字編輯器，在標頭區加入下面的文字，就放在 `</head>` 結尾標籤前：

```html
<script>
 // JavaScript 將放在這裡
</script>
```

4. 現在，我們將在我們的 `<script>` 元素中加入一些 JavaScript 程式，讓網頁能做些有趣的事，接者在「// JavaScript 將放在這裡」那行後面：

```js
document.addEventListener("DOMContentLoaded", function () {
  function createParagraph() {
    let para = document.createElement("p");
    para.textContent = "You clicked the button!";
    document.body.appendChild(para);
  }

  const buttons = document.querySelectorAll("button");

  for (let i = 0; i < buttons.length; i++) {
    buttons[i].addEventListener("click", createParagraph);
  }
});
```

5. 儲存你的檔案並且重新整理網頁，現在你會發現每次點擊按鈕，都會在下方產生一個新的文字段落。

**備註：** 如果你的版本不能正常運作，重新按照步驟再操作一次，檢查每一步都正確。你下載的範例是 `.html` 結尾的檔名？你加入的 `<script>` 元素在 `</head>` 標籤的前面？你輸入的 JavaScript 與上面提供的一模一樣？**JavaScript 程式大小寫，而且很挑剔，所以你輸入的語法要一模一樣，不然可能會無法運作。**

**備註：** GitHub 上有完整版本的範例在 apply-javascript-internal.html（看線上版本）。

### 外部的 JavaScript

內部 JavaScript 目前運作得很好，但如果我們想把 JavaScript 放在外部檔案，應該怎麼做？讓我們現在來探索。

1. 首先，建立一個新的檔案，和 HTML 檔案放在相同的目錄下，命名為 `script.js` ，確定這個檔案是以 .js 為副檔名， 因為這就是它被識別為 JavaScript 的原因。
2. 將 `<script>` 元素（包含裡面的程式）換成下面的樣子：

```html
<script src="script.js" async></script>
```

3. 在 `script.js` 中，加入下面的程式碼：

```js
function createParagraph() {
  let para = document.createElement("p");
  para.textContent = "You clicked the button!";
  document.body.appendChild(para);
}

const buttons = document.querySelectorAll("button");

for (let i = 0; i < buttons.length; i++) {
  buttons[i].addEventListener("click", createParagraph);
}
```

4. 儲存檔案並在你的瀏覽器執行重新整理，你應該會看到一樣的結果！雖然是一樣的結果，但現在我們是由外部的檔案來引入 JavaScript 程式。就組織程式碼，讓程式可以在多個 HTML 間重複被使用而言，這通常是好的作法。另外，因為少了一大堆程式碼在裡頭，也能夠讓 HTML 檔案更容易被閱讀。

**備註：** 你可以在 GitHub 上找到這個版本的 apply-javascript-external.html 與 script.js （看線上版本）。

### 行內的 JavaScript

注意，有時候你會遇到一些 JavaScript 程式混在 HTML 語法之中。它可能會看起來像這樣：

```js
function createParagraph() {
  var para = document.createElement("p");
  para.textContent = "You clicked the button!";
  document.body.appendChild(para);
}
```

```html
<button onclick="createParagraph()">Click me!</button>
```

你可以在底下試試這個段程式的作用。

這個範例與前面兩個有完全相同的功能，除了 `<button>` (en-US) 元素中包含了一個行內 `onclick` 處理程序，當按鈕按下時執行。

\*\*然而，請不要這樣做。\*\*用 JavaScript 汙染你的 HTML 是一種不好的作法，而且沒有效率，因為你必須在每個你希望 JavaScript 作用的地方加入 `onclick="createParagraph()"` 屬性。

使用單純 JavaScript 的結構，讓你可以用一個指令選擇所有的按鈕。在我們前面的範例中，使用下面的程式碼達到這個目的：
```

var buttons = document.querySelectorAll('button');

for (var i = 0; i < buttons.length ; i++) {
  buttons[i].addEventListener('click', createParagraph);
}
```

這或許看起來比 `onclick` 屬性還長一點，但是能套用在全部的按鈕上，無論頁面上有多少按鈕，也不管未來新增或移除多少個（按鈕）。這段 JavaScript 程式都不需要改變。

**備註：** 試著編輯你自己版本的 `apply-javascript.html` ，在裡面多添加一點按鈕。當你重新載入網頁，你應該會發現所有按鈕，按下去的時候都會建立一的段落。很簡潔吧！

### 腳本載入策略

在正確的時機載入腳本涉及一些要注意的東西。並不如它看起來的簡單！其中一個常見的問題是，所有的 HTML 是根據出現順序載入。假如你使用 JavaScript 操作頁面中的元素（精確地來說是 DOM 元素 (en-US)），如果 JavaScript 在這些 HTML 操作對象前被讀取及解析，你的程式將無法運作。

上面的範例中，內部和外部 JavaScript 範例裡的程式碼都放在 HTML 的 head 區域，在 body 區被載入前就先被解析。這樣會造成一些問題，所以我們載入與執行在文件(HTML 檔)的開頭，是 HTML `body` 還沒解析之前。這可能會產生錯誤，所以我們使用一些模式來處理它。

在內部程式的範例中，你可以看到以下這樣結構的程式碼：

```js
document.addEventListener("DOMContentLoaded", function() {
  ...
});
```

這是一個事件偵聽器，它偵聽瀏覽器的「DOMContentLoaded」事件，它是在 HTML body 部分已經完全載入與解析發出。區塊內（... 的部分）的 JavaScript 直到事件被發出後才會執行，這樣子問題就被避開了。（你將會在這個課程中學習到什麼是事件 (en-US)）

在外部程式的範例中，我們使用比較現代的 JavaScript 特性來解決這個問題，`defer` 屬性，它告訴瀏覽器碰到這種 `<script>` 標籤時，繼續下載後面其他的 HTML 內容。

```js
<script src="script.js" defer></script>
```

在這個例子中，腳本（JavaScript 程式）與 HTML 會同時載入，所以程式將正確地執行。

**備註：** 在外部程式的範例裡，我們不需要使用 `DOMContentLoaded` 事件因為 `defer` 為我們解決問題了。而在內部程式的範例裡我們沒用 `defer` 屬性，是因為 `defer` 屬性只能用於外部的腳本。

這個問題有另一個舊式的解決方法，就是將 `script` 元素放在 `body` 元素的底部（剛好在 `</body>` 的前面），如此它就會在所有 HTML 被解析完之後才被載入。這個方法的問題在於腳本的載入與解析工作會被完成擋住，一直到所有 HTML 載入完成。在擁有許多 JavaScript 的大型網站中，這樣會導致嚴重的效能問題，拖慢你的網站。

#### async 和 defer

實際上，有兩個方法可以閃過上述腳本被擋到的問題：`async` 與 `defer`（前面看到的）。來看看兩者的區別。

使用 `async` 屬性（如下所示）所載入的腳本，在下載的同時不會讓網頁的渲染被阻塞（停住），並且在下載完成後馬上執行。它並不保證腳本會按照任何特定的順序執行，只保證不去妨礙網頁中其他部分顯示工作。`async` 的最佳使用情境，是當網頁中的腳本間彼此獨立，因而不依賴彼此運行的狀況下。

例如你有以下的 `script` 元素

```html
<script async src="js/vendor/jquery.js"></script>

<script async src="js/script2.js"></script>

<script async src="js/script3.js"></script>
```

你不能將元素的順序視為腳本載入順序。 `jquery.js` 可能會在 `script2.js` 與 `script3.js` 的之前或之後載入，在這個情況下，在腳本中依賴 `jquery` 的函數將會發生錯誤，因為被執行到的時候 `jquery` 還沒被定義（載入且解析完畢）。

當有許多非立即要使用的腳本，而你只希望它們能盡快被載入完畢，就應該使用 `async` 。例如：你有一些遊戲內容的檔案需要載入，它將在遊戲開始後被用到。但是現在你只是需要顯示遊戲介紹、一些選單以及遊戲大廳，不希望等到所有內容都下載完成之後才顯示。

使用 `defer` 屬性（如下所示）所載入的腳本，會在腳本與內容都下載完成後，依照出現順序被執行。

```html
<script defer src="js/vendor/jquery.js"></script>

<script defer src="js/script2.js"></script>

<script defer src="js/script3.js"></script>
```

全部具有 `defer` 屬性的腳本會依據出現的順序載入。因此在第二個範例中，我們可以肯定 `jquery.js` 會在 `script2.js` 與 `script3.js` 之前被載入， `script2.js` 會在 `script3.js` 之前載入。在網頁的所有內容被載入完成之前，它是不會被執行的。當你的程式依賴著某些元素存在時（如：要調整頁面上一到多個元素），這個屬性很有用。

總結一下：

* `async` 和 `defer` 屬性都是用來告訴瀏覽器使用獨立線程（行程）來下載腳本，同一時間頁面的其它部分（如 DOM 元件之類）也在下載，因此頁面的載入不會因為腳本被影響。
* 如果你的腳本應該立即被執行，而且不依賴其它腳本先被載入，就用 `async` 。
* 如果你的腳本依賴其他腳本先被解析或 DOM 已經存在，就用 `defer` 來載入它們，並根據你想要瀏覽器執行的順序安排 `<script>` 元素的次序。
註解
--

如同 HTML 和 CSS，在 JavaScript 的程式碼中也可以撰寫註解，它會被瀏覽器忽略，僅用來提供你的開發伙伴，說明程式是如何運作（或是自己，當六個月後回來看程式碼，忘記曾經做過了什麼）。註解非常有用，你應該常常使用它，尤其是在大型應用程式裡。註解有兩種形式：

* 單行註解，寫在兩個（正）斜線「//」之後，例如：

```js
// 我是一個註解
```

* 多行註解，寫在 /\* 和 \*/ 之間的文字，例如：

```js
/\*
 我也是
 一個註解
\*/
```

舉例來說，我們可以在前面範例的 JavaScript 裡加入註解，像是：

```js
// 函數：建立一個段落元素並加到 HTML body 的尾端

function createParagraph() {
  var para = document.createElement("p");
  para.textContent = "You clicked the button!";
  document.body.appendChild(para);
}

/\*
 1. 找出頁面上所有按鈕，並把它們放到陣列中
 2. 使用迴圈，對每個按鈕偵聽 click 事件

 當任何按鈕被按下，執行 createParagraph() 函數
\*/

var buttons = document.querySelectorAll("button");

for (var i = 0; i < buttons.length; i++) {
  buttons[i].addEventListener("click", createParagraph);
}
```

**備註：** 一般而言，多寫註解比少寫來得好。但是要注意，如果你發現加了許多註解在說明變數的用途（那麼你的變數命名可能需要更直觀，更帶有意義），或是解釋非常簡單的操作（也許你的程式碼太過於複雜）。

總結
--

所以你已經踏出在 JavaScript 世界中的第一步。我們從理論開始，逐漸熟悉使用 JavaScript 的原因，以及你可以用它做些什麼。過程中你看到了一些程式碼範例，學到如何將 JavaScript 與你網站的其它東西放在一起。

JavaScript 目前可能看起來有一點嚇人，然而不用擔心，在本課程我們會透過簡單的步驟，帶著你建立觀念並繼續向前。 在下一章節，我們將會投入更實用的知識 (en-US)，帶你直接入門並建立你自己的 JavaScript 作品。

* Overview: First steps
* 次頁