運算式與運算子 - JavaScript

運算式與運算子
=======

* « 前頁
* 次頁 »

這個章節將講述 JavaScript 的運算式與運算子，包括賦值運算子，比較運算子，算術運算子，位元運算子， 邏輯運算子, 字串運算子, 條件(三元)運算子 以及更多運算子.

更多關於運算子以及運算式的資料可以在 reference 中找到。

運算子
---

JavaScript 有以下幾種運算子。 此處將描述運算子以及一些運算子的優先順序。

* 賦值運算子
* 比較運算子
* 算術運算子
* 位元運算子
* 邏輯運算子
* 字串運算子
* 條件（三元）運算子
* 逗點運算子
* 一元運算子
* 關係運算子

JavaScript 同時具有二元運算子及一元運算子， 以及一種特殊的 三元運算子，也就是 條件運算子。 一個二元運算子需要具備兩個運算元， 一個在運算元之前，一個在運算元之後:
```

運算元1 運算子 運算元2
```

例如, `3+4` 或 `x*y`.

一個 一元運算子 需要一個運算元， 位於運算子之前或之後:
```

運算子 運算元
```

或
```

運算元 運算子
```

例如， `x++` 或 `++x`.

### 賦值運算子

一個 賦值運算子 (en-US) 將 基於其 右方的運算元 的值賦予其 左方的運算元。 最簡單的 賦值運算子 是 等於 (`=`)， 它將賦予 左方運算元 與 右方運算元相同之值。 也就是， `x = y` 會把 y 的值賦予給 x。

也有一些復合的 賦值運算子 是為了縮短下面表中的運算:

| 名稱 | 簡化的運算子 | 意義 |
| --- | --- | --- |
| 賦值 (en-US) | `x = y` | `x = y` |
| 加法 (en-US)賦值 (en-US) | `x += y` | `x = x + y` |
| 減法 (en-US)賦值 (en-US) | `x -= y` | `x = x - y` |
| 乘法 (en-US)賦值 (en-US) | `x *= y` | `x = x * y` |
| 除法 (en-US)賦值 (en-US) | `x /= y` | `x = x / y` |
| 餘數 (en-US)賦值 (en-US) | `x %= y` | `x = x % y` |
| 指數 (en-US)賦值 (en-US) | `x **= y` | `x = x ** y` |
| 左移賦值 (en-US) | `x <<= y` | `x = x << y` |
| 右移賦值 (en-US) | `x >>= y` | `x = x >> y` |
| 無號右移賦值 (en-US) | `x >>>= y` | `x = x >>> y` |
| 位元 AND 賦值 (en-US) | `x &= y` | `x = x & y` |
| 位元 XOR 賦值 (en-US) | `x ^= y` | `x = x ^ y` |
| 位元 OR 賦值 (en-US) | `x |= y` | `x = x | y` |

#### 解構

為了進行更複雜的賦值，解構賦值是 JavaScript 用來從陣列或物件中提取資料的語法。

```js
var foo = ["one", "two", "three"];

// 不使用解構
var one = foo[0];
var two = foo[1];
var three = foo[2];

// 使用解構
var [one, two, three] = foo;
```

### 比較運算子

比較運算子 (en-US) 會比較 運算元 並基於比較的結果回傳邏輯值。 運算元可以是數字，字串，邏輯，或物件的值。 字串的比較是基於字典序的， 使用 Unicode 的值。 在多數情況下，假如兩個運算元不具有相同型態， JavaScript 會嘗試將它們轉換成相同型態。這個行為通常是將運算元以數學形式對待。 在某些的轉換型態的例外中會使用到 `===` 及 `!==` 運算子， 它們會嚴格地進行相等或不相等的比較。 這些運算子不會在確認相等與否前嘗試轉換運算元的型態。 下面的表解釋了比較運算子:

```js
var var1 = 3;
var var2 = 4;
```

| 運算子 | 描述 | 會回傳 True 的例子 |
| --- | --- | --- |
| 等於 (en-US) (`==`) | 假如運算元等價就回傳 True。 | `3 == var1` `"3" == var1` `3 == '3'` |
| 不等於 (en-US) (`!=`) | 假如運算元等價就回傳 True。 | `var1 != 4 var2 != "3"` |
| 嚴格等於 (en-US) (`===`) | 假如運算元具有相同型態且等價則回傳 True。參考 `Object.is` (en-US) 及 JS 中的等價性。 | `3 === var1` |
| 嚴格不等於 (en-US) (`!==`) | 假如運算元具有相同型態但不等價，或是具有不同型態，回傳 True。 | `var1 !== "3" 3 !== '3'` |
| 大於 (en-US) (`>`) | 假如左方運算元大於右方運算元，回傳 True。 | `var2 > var1 "12" > 2` |
| 大於或等於 (en-US) (`>=`) | 假如左方運算元大於或等於右方運算元，回傳 True。 | `var2 >= var1 var1 >= 3` |
| 小於 (en-US) (`<`) | 假如左方運算元小於右方運算元，回傳 True。 | `var1 < var2 "2" < 12` |
| 小於或等於 (en-US) (`<=`) | 假如左方運算元小於或等於右方運算元，回傳 True。 | `var1 <= var2 var2 <= 5` |

**備註：** `=>` 不是運算子，是 箭頭函式。

### 算術運算子

算術運算子 (en-US) 以 數值 (文字或變數也可以)作為其運算元，並回傳單一數值。最常見的算術運算元是 加法 (`+`)，減法 (`-`)， 乘法 (`*`)，及除法 (`/`)。 這些運算子在大多數程式語言中功能相同 (比較特別的是，在除數為 0 時 `Infinity`)。例如:

```js
1 / 2; // 0.5
1 / 2 == 1.0 / 2.0; // 會是 true
```

除了標準的算術運算子外 (+, -, \* /)， JavaScript 提供以下表中的算術運算子:

| 運算子 | 描述 | 範例 |
| --- | --- | --- |
| 取餘數 (en-US) (`%`) | 二元運算子。回傳兩個運算元相除後的餘數。 | 12 % 5 回傳 2. |
| 增加 (en-US) (`++`) | 一元運算子。 將運算元增加 1。假如使用在運算元之前 (`++x`)，會運算元回傳增加 1 後的值;假如使用在運算元之後。 (`x++`)`，` 會回傳運算元加 1 前的值。 | 假如 `x是` 3，那 `++x` 將把 `x` 設定為 4 並回傳 4，而 `x++ 會回傳` 3 ， 接著才把 `x 設定為` 4。 |
| 減少 (en-US) (`--`) | 一元運算子。 將運算元減少 1。回傳值的情況與 增加運算元 相同。 | 假如 `x是` 3，那 `--x` 將把 `x` 設定為 2 並回傳 2，而 `x-- 會回傳` 3 ， 接著才把 `x 設定為` 2。 |
| (一元運算子)減號 (en-US) (`-`) | 一元運算子。回傳運算元的負數。 | 假如 x 是 3，-x 回傳 -3。 |
| (一元運算子)加號 (en-US) (`+`) | 一元運算子。嘗試將運算元轉換成數字，假如它還不是數字的話。 | `+"3"` `回傳 3`。 `+true` 回傳 `1.` |
| 指數運算子 (en-US) (`**`)
實驗性質
 | 計算以 a 為底的 `b` 次方， 也就是, `a^b` | `2 ** 3` `回傳 8`. `10 ** -1` 回傳 `0.1`. |

### 位元運算子

位元運算子 (en-US) 把運算元當作 32 位元的集合來看待 (0 和 1)， 而不是十進位，十六進位，或八進位。例如，十進位數字 9 以二進位表示就是 1001。 位元運算子將運算元以上述二進位的形式處理，但是回傳 Javascript 中的數字類型值。

下列表總結了 JavaScript' 中的位元運算子。

| 運算子 | 用法 | 描述 |
| --- | --- | --- |
| 位元 AND (en-US) | `a & b` | 回傳兩個運算元對於每個 bit 做 AND 的結果。 |
| 位元 OR (en-US) | `a | b` | 回傳兩個運算元對於每個 bit 做 OR 的結果。 |
| 位元 XOR (en-US) | `a ^ b` | 回傳兩個運算元對於每個 bit 做 XOR 的結果。 |
| 位元 NOT (en-US) | `~ a` | 將運算元中的每個 bit 反轉(1->0,0->1)。 |
| 左移 (en-US) | `a << b` | 將 `a` 的每個 bit 向左移動 `b` 個 bits，空餘的位數以 0 填滿。 |
| 有號右移 (en-US) | `a >> b` | 將 `a` 的每個 bit 向右移動 `b` 個 bits，空餘位數以最高位補滿。 |
| 以 0 填充的右移 (en-US) | `a >>> b` | 將 `a` 的每個 bit 向右移動 `b` 個 bits，空餘的位數以 0 填滿。 |

#### 位元邏輯運算子

概念上，位元邏輯運算子運作過程如下:

* 運算元被轉換為 32 bits 的整數以二進位形式表示 (0 和 1)。大於 32 bits 的數字將被捨棄多出來的位元。例如， 下列整數大於 32 個 bit 但是會被轉換為 32 個 bit 的整數:
```

轉換之前:  11100110111110100000000000000110000000000001
轉換之後:              10100000000000000110000000000001
```

* 第一個運算元中的每個 bit 分別對應到第二個運算元的每個 bit: 第一個 bit 對 第一個 bit, 第二個 bit 對 第二個 bit， 以此類推。
* 運算子會對於 bit 進行運算， 結果也是基於 bit 來決定的。

例如， 9 的二元表示法是 1001， 15 的二元表示法是 1111。因此，在使用位元運算子的時候，結果如下:

| 運算式 | 結果 | 二元描述式 |
| --- | --- | --- |
| `15 & 9` | `9` | `1111 & 1001 = 1001` |
| `15 | 9` | `15` | `1111 | 1001 = 1111` |
| `15 ^ 9` | `6` | `1111 ^ 1001 = 0110` |
| `~15` | `-16` | `~ 0000 0000 … 0000 1111 = 1111 1111 … 1111 0000` |
| `~9` | `-10` | `~ 0000 0000 … 0000 1001 = 1111 1111 … 1111 0110` |

注意，在使用 位元 NOT 運算子時， 所有的 32 個 bit 都被進行 NOT 了，包含最左邊用來描述正負數的位元(two's-complement representation)。

#### 位元移動運算子

位元移動運算子需要兩個運算元: 第一個是運算的目標，第二個是要移動的位元數。移動的方向取決於使用的運算子。

移動運算子會將運算元轉換成 32 bits 的整數，並且會回傳與左方運算元相同的型態。

移動運算子在下表被列出.

| 運算子 | 描述 | 範例 |
| --- | --- | --- |
| 左移 (en-US) (`<<`) | 這個運算子會將第 一個運算元的每個 bit 向左移動 第二個運算元所指定的 bit 數量。左邊超出的位數會被捨棄，右邊空出的位數以 0 補齊。 | `9<<2` 得到 36，因為 1001 向左移動 2 bits 會得到 100100， 也就是二進位的 36。 |
| 有號右移 (en-US) (`>>`) | 這個運算子會將第 一個運算元的每個 bit 向右移動 第二個運算元所指定的 bit 數量。右邊超出的位數會被捨棄，左邊空出的位數以最高位補齊。 | `9>>2` 得到 2，因為 1001 向右移動 2 bits 會得到 10，也就是二進位的 2。 相同的， `-9>>2` 會得到 -3，因為最高位用來表示正負號的 bit 被保留了。 |
| 以 0 填充的右移 (en-US) (`>>>`) | 這個運算子會將第 一個運算元的每個 bit 向右移動 第二個運算元所指定的 bit 數量。右邊超出的位數會被捨棄，左邊空出的位數以 0 補齊。 | `19>>>2 得到` 4， 因為 10011 向右移動 2 bits 會得到 100，是二進位的 4。對於非負的數字而言， 以 0 填充的右移 會得到和 有號右移相同的結果。 |

### 邏輯運算子

邏輯運算子 (en-US) 通常被用於布林(邏輯)值; 使用於 布林(邏輯)值時， 它們會回傳布林型態的值。 然而，`&&` 和 `||` 運算子實際上是回傳兩指定運算元之一，因此用於非布林型態值時，它可能會回傳一個非布林型態的值。 邏輯運算子將在下表中被詳細解釋。

| Operator | Usage | Description |
| --- | --- | --- |
| 邏輯 AND (en-US) (`&&`) | `運算式1 && 運算式2` | 假如 `運算式1` 可以被轉換成 false 的話，回傳 `運算式1`; 否則，回傳 `運算式2`。 因此，`&&`只有在 兩個運算元都是 True 時才會回傳 True，否則回傳 `false`。 |
| 邏輯 OR (en-US) (`||`) | `運算式1 || 運算式2` | 假如 `運算式1` 可以被轉換成 true 的話，回傳 `運算式1`; 否則，回傳 `運算式2`。 因此，`||`在 兩個運算元有任一個是 True 時就會回傳 True，否則回傳 `false`。 |
| 邏輯 NOT (en-US) (`!`) | `!運算式` | 假如單一個運算元能被轉換成 True 時，回傳`false` ， 不然回傳 `true`。 |

可以被轉換為 false 的運算式是 null、0、NaN、空字串（""），或 undefined。

下面是 `&&`（邏輯 AND）運算子的範例。

```js
var a1 = true && true; // t && t 回傳 true
var a2 = true && false; // t && f 回傳 false
var a3 = false && true; // f && t 回傳 false
var a4 = false && 3 == 4; // f && f 回傳 false
var a5 = "Cat" && "Dog"; // t && t 回傳 Dog
var a6 = false && "Cat"; // f && t 回傳 false
var a7 = "Cat" && false; // t && f 回傳 false
```

下列是 `||`（邏輯 OR）運算子的範例。

```js
var o1 = true || true; // t || t 回傳 true
var o2 = false || true; // f || t 回傳 true
var o3 = true || false; // t || f 回傳 true
var o4 = false || 3 == 4; // f || f 回傳 false
var o5 = "Cat" || "Dog"; // t || t 回傳 Cat
var o6 = false || "Cat"; // f || t 回傳 Cat
var o7 = "Cat" || false; // t || f 回傳 Cat
```

下列是 `!`（邏輯 NOT）運算子的範例。

```js
var n1 = !true; // !t 回傳 false
var n2 = !false; // !f 回傳 true
var n3 = !"Cat"; // !t 回傳 false
```

#### 短路解析

邏輯運算式是由左向右解析的， 他們會以下列規則嘗試進行 短路解析:

* `false` && *任何東西* 是 false 的短路解析。
* `true` || *任何東西* 是 true 的短路解析。

這些規則保證 解析總是正確的。 值得注意的地方是，剩餘部分的運算式並沒有被解析，所以不會占用任何效能。

### 字串運算子

除了作為比較運算子之外， 運算子 (+) 也能用於字串，將兩字串接在一起，並回傳接在一起後的結果。

例如，

```js
console.log("我的 " + "字串"); // 會印出 字串 "我的字串"。
```

簡化的設定運算子 += 也能用於串接字串。

例如，

```js
var mystring = "字";
mystring += "母"; // 得到 "字母" 並賦與給變數 mystring.
```

### 條件（三元）運算子

條件運算子 是 JavaScript 中唯一需要三個運算元的運算子。 這個運算子接受兩個運算元作為值且一個運算元作為條件。 語法是:
```

條件 ? 值1 : 值2
```

如果 *條件* 為 true，運算子回傳 *值 1，* 否則回傳 *值 2。* 你可以在任何使用標準運算子的地方改用 條件運算子。

例如，

```js
var status = age >= 18 ? "成人" : "小孩";
```

這個陳述句會將 "成人" 賦與給變數 `status` 假如 `age` 大於等於 18。 否則，會將 "小孩" 賦與給變數 `status`。

### 逗號運算子

逗點運算子 (en-US) (`,`) 作用是解析兩個運算元並回傳後面那個運算元的值。 這個運算子通常用於 for 迴圈內部，讓多個變數能在每次迴圈中被更新。

例如，假如 `a` 是一個有十個物件在裡面的二維陣列， 下面的程式中就使用了逗點運算子來同時更新兩個變數。 這段程式碼會印出陣列中所有對角線上的物件:

```js
for (var i = 0, j = 9; i <= j; i++, j--)
  console.log("a[" + i + "][" + j + "]= " + a[i][j]);
```

### 一元運算子

一元運算 是只需要一個運算元的運算。

#### `delete`

`delete` 運算子會刪除物件，物件的性質，或是陣列中指定 index 的物件。 語法是:

```js
delete 物件名稱;
delete 物件名稱.性質;
delete 物件名稱[索引];
delete 性質; // 只有在 with 陳述句中可以使用
```

`物件名稱` 是物件的名稱， 性質 是物件中的一個特性， 索引 是用來表示物件在陣列中位置的一個整數。

第四種形式只有在 `with` (en-US) 陳述句中可用， 用來刪除物件中的一個特性。

你可以用 `delete` 運算子來刪除隱式宣告的變數， 但不適用於使用 var 宣告的變數。

假如 `delete` 運算子使用成功， 它會將物件 或是 物件的特性設定為 `未定義。` `delete` 運算子會在運算成功時回傳 true ，失敗時回傳 `false` 。

```js
x = 42;
var y = 43;
myobj = new Number();
myobj.h = 4; // 建立特性 h
delete x; // 回傳 true (只有在隱式宣告時能被刪除)
delete y; // 回傳 false (在使用 var 宣告時無法刪除)
delete Math.PI; // 回傳 false (不能刪除內建定義的特性)
delete myobj.h; // 回傳 true (可以刪除使用者自定義的特性)
delete myobj; // 回傳 true (在隱式宣告時可被刪除)
```

##### 刪除陣列元素

在你刪除了陣列中的一個元素後， 陣列的長度並不會改變。 例如， 假如你`刪除 a[3]`， `a[4]` 依然是 `a[4]` 而 `a[3]` 為 未定義。

當使用 `delete` 運算子刪除陣列中的一個元素後， 那個元素便不再存在於陣列中了。 在下面的程式中， `trees[3]` 被用 delete 移除了。然而， `trees[3]` 的記憶體位址仍可用並且會回傳 未定義。

```js
var trees = ["redwood", "bay", "cedar", "oak", "maple"];
delete trees[3];
if (3 in trees) {
  // 不會執行到這裡
}
```

假如你希望給予陣列元素 未定義 的值， 你可以直接使用 `undefined` 關鍵字而不是使用 delete 運算子。 下列範例中， `trees[3]` 被指定了 `undefined`， 然而陣列元素依然存在:

```js
var trees = ["redwood", "bay", "cedar", "oak", "maple"];
trees[3] = undefined;
if (3 in trees) {
  // 會執行這裡
}
```

#### `typeof`

`typeof` 運算子 能以下列任一方式使用:
```

typeof 運算元
typeof (運算元)
```

`typeof` 運算子會回傳代表運算元類型的 字串。 `運算元能是字串，變數，關鍵字，或是會回傳型態的物件。` 括號是可有可無的。

假設你定義了以下這些變數:

```js
var myFun = new Function("5 + 2");
var shape = "round";
var size = 1;
var today = new Date();
```

`typeof` 運算子會回傳下列結果:

```js
typeof myFun; // 回傳 "function"
typeof shape; // 回傳 "string"
typeof size; // 回傳 "number"
typeof today; // 回傳 "object"
typeof doesntExist; // 回傳 "undefined"
```

對於 `true` 和 `null關鍵字，` `typeof` 運算子會回傳下列結果:

```js
typeof true; // 回傳 "boolean"
typeof null; // 回傳 "object"
```

對於字串或數字， `typeof` 運算子會回傳下列結果:

```js
typeof 62; // 回傳 "number"
typeof "Hello world"; // 回傳 "string"
```

對於特性，`typeof` 運算子會回傳 特性的值的類型:

```js
typeof document.lastModified; // 回傳 "string"
typeof window.length; // 回傳 "number"
typeof Math.LN2; // 回傳 "number"
```

對於 方法 及 函式， `typeof` 運算子會回傳下列結果:

```js
typeof blur; // 回傳 "function"
typeof eval; // 回傳 "function"
typeof parseInt; // 回傳 "function"
typeof shape.split; // 回傳 "function"
```

對於內建定義的物件， `typeof` 運算子會回傳下列結果:

```js
typeof Date; // 回傳 "function"
typeof Function; // 回傳 "function"
typeof Math; // 回傳 "object"
typeof Option; // 回傳 "function"
typeof String; // 回傳 "function"
```

#### `void`

`void` 運算子 (en-US) 能以下列任一方式使用:
```

void (運算式)
void 運算式
```

`void` 運算子會解析運算式而不回傳任何值。 `運算式` 是 JavaScript 中要解析的對象。 括號是可有可無的，但是建議使用。

你可以使用 `void` 運算子來解析超連結中的運算式。 運算式會被解析而不會在當前頁面被印出。

下列範例是一個在點擊時甚麼都不做的超連結。 當使用者點擊連結時， `void(0)` 被解析為 未定義， 而甚麼都不會發生。

```html
<a href="javascript:void(0)">點擊這裡，甚麼都不會發生</a>
```

下列範例是一個在使用者點擊時傳送表單的超連結。

```html
<a href="javascript:void(document.form.submit())"> 點擊以送出</a>
```

### 關係運算子

關係運算子比較兩運算元並基於比較結果回傳布林值。

#### `in`

`in` 運算子 (en-US)在指定性質存在於物件中時回傳 true。語法是:

```js
性質名稱 in 物件名稱
```

性質名稱 可以是 字串或數字，或是陣列的索引， 且物件名稱是物件的名稱。

下列範例示範了 `in` 運算子的一些用法。

```js
// 陣列
var trees = ["redwood", "bay", "cedar", "oak", "maple"];
0 in trees; // 回傳 true
3 in trees; // 回傳 true
6 in trees; // 回傳 false
"bay" in trees; // 回傳 false (你必須指定 索引，
// 而不是 索引所對應的元素)
"length" in trees; // 回傳 true (length 是陣列的性質之一)

// 內建物件
"PI" in Math; // 回傳 true
var myString = new String("coral");
"length" in myString; // 回傳 true

// 自訂義物件
var mycar = { make: "Honda", model: "Accord", year: 1998 };
"make" in mycar; // 回傳 true
"model" in mycar; // 回傳 true
```

#### `instanceof`

`instanceof` 運算子 (en-US) 在 指定物件 具有 指定的物件型態 時回傳 true。 語法是:
```

物件名稱 instanceof 物件類型
```

`物件名稱` 是用來與 物件類型 比較的物件的名字， 物件類型 是物件的類型， 例如 `Date` 或 `Array`。

當你需要在程式執行中確認物件的形態時，你可以使用 ins`tanceof` 運算子。 例如，當捕捉到例外時， 你可以依照例外的類型來決定用來處理意外的程式碼。

例如，下列程式碼使用 `instanceof` 來判斷變數 `theDay` 是不是 `Date` 類型的物件。 因為 `theDay` 是 `Date` 類型的物件， 所以 if 陳述中的陳述句會被執行。

```js
var theDay = new Date(1995, 12, 17);
if (theDay instanceof Date) {
  // 會被執行的陳述
}
```

### 運算子優先級

運算子優先級決定運算子被使用於運算元的先後順序。 你也可以使用括號來強制指定優先級。

下列表格列出了運算子的優先級， 從高到低。

| 運算子類型 | 屬於該類別的運算子 |
| --- | --- |
| 成員 | `. []` |
| 呼叫/建立 實例 | `() new` |
| 反向/增加 | `! ~ - + ++ -- typeof void delete` |
| 乘法/除法 | `* / %` |
| 加法/減法 | `+ -` |
| 位元移動 | `<< >> >>>` |
| 關係運算子 | `< <= > >= in instanceof` |
| 相等性 | `== != === !==` |
| 位元 and | `&` |
| 位元 xor | `^` |
| 位元 or | `|` |
| 邏輯 and | `&&` |
| 邏輯 or | `||` |
| 條件運算子 | `?:` |
| 指定運算子 | `= += -= *= /= %= <<= >>= >>>= &= ^= |=` |
| 逗點運算子 | `,` |

這個表格更詳細的版本，解釋了運算子的更多細節和關聯性， 可以在 JavaScript 參考 中被找到。

運算式
---

運算式是任何一段可以取得一個值的程式碼。

任何合乎語法的運算式都能取得一個值，概念上， 有兩種不同型態的運算式: 有副作用的 (例如: 將一個值指定給一個變數) 以及只為了取得值而解析的運算式。

運算式 `x = 7` 是上述的第一種類型。這個使用 = 運算子的運算式會將數值 7 賦與給 x。 運算式本身也會被解析為 7。

運算式 `3 + 4` 是上述的第二種類型。這個運算式使用 + 運算子把 3 和 4 加起來，而不指定給任何變數。

JavaScript 運算式有下列幾種種類:

* 算術: 解析出數字， 例如 3.14159。（通常使用算術運算子。）
* 字串: 解析出字串， 例如 "Fred" or "234"。（通常使用字串運算子。）
* 邏輯: 解析出 True 或 False（通常與邏輯運算子相關。）
* 主流運算式: JavaScript 基本的關鍵字及運算式。
* 左側運算式: 左側是指定值的對象。
### 主流運算式

JavaScript 基本的關鍵字及運算式。

#### `this`

`this` 關鍵字 能取得當前所在物件。 一般而言， `this` 能取得呼叫處所在的物件。 你可以使用 點 或是 中括號 來取用該物件中的特性:
```

this['特性名稱']
this.特性名稱
```

以下定義一個叫做 `validate` 的函式，比較物件中特性 `value 與傳入的兩變數`:

```js
function validate(obj, lowval, hival) {
  if (obj.value < lowval || obj.value > hival) console.log("不可用的值!");
}
```

你可以在表單的 `onChange` event handler 中呼叫 `validate` 函式， 並以 `this` 來傳入表單的元素， 範例如下:

```html
<p>請輸入一介於18 與 99 的數字:</p>
<input type="text" name="age" size="3" onChange="validate(this, 18, 99);" />
```

#### 分組運算子

分組運算子 `( )` 控制了運算子的優先順序。 例如，你可以覆寫先乘除，後加減的優先順序，使其變成先加減，後乘除。

```js
var a = 1;
var b = 2;
var c = 3;

// 預設運算級
a + b \* c; // 7
// 預設的結果
a +
  (b \* c)(
    // 7

    // 現在複寫運算級
    // 變成先進行加法，後乘法了
    a + b,
  ) \*
    c; // 9

// 結果
a \* c + b \* c; // 9
```

#### 解析

解析是 JavaScript 中的一個實驗性功能， 在未來版本的 ECMAScript 計畫被導入。有兩種不同類型的解析:

實驗性質 `[for (x of y) x]` (en-US)

陣列解析。

實驗性質 `(for (x of y) y)` (en-US)

產生器解析。

解析在許多程式語言中都存在，允許你快速地基於現存陣列產生新的陣列，例如:

```js
[for (i of [ 1, 2, 3 ]) i\*i ];
// [ 1, 4, 9 ]

var abc = [ 'A', 'B', 'C' ];
[for (letters of abc) letters.toLowerCase()];
// [ 'a', 'b', 'c' ]
```

### 左側運算式

左側是指定值的對象。

#### `new`

你可以使用 `new` 運算子 來建立一個使用者自定義物件或內建物件的實例。 用法如下:

```js
var 物件名稱 = new 物件型態([參數1, 參數2, ..., 參數N]);
```

#### super

super 關鍵字用於呼叫物件的父物件中的函式。在使用類別來呼叫父類別的建構子時很實用，例如:
```

super([參數]); // 呼叫父物件的建構子.
super.父物件的函式([參數]);
```

#### 展開運算子

展開運算子 (en-US)能將運算式展開於需要多個參數的地方（如函式呼叫）或是需要多個元素（如陣列字串常數）的地方。

**範例**：現在你想要用已存在的一個陣列做為新的一個陣列的一部份，當字串常數不再可用而你必須使用指令式編程，也就是使用，一連串的 `push`、`splice`、`concat`，等等。展開運算子能讓過程變得更加簡潔:

```js
var parts = ["肩膀", "膝蓋"];
var lyrics = ["頭", ...parts, "和", "腳趾"];
```

相同的，展開運算子也適用於函式呼叫:

```js
function f(x, y, z) {}
var args = [0, 1, 2];
f(...參數);
```

* « 前頁
* 次頁 »


Loops and iteration - JavaScript

Loops and iteration
===================

* « 前頁
* 次頁 »

迴圈提供一個快速又簡潔的方法來重複地做某件事。這個章節的JavaScript 教學會介紹在 JavaScript 可以使用的幾種不同的迭代陳述式。

你可以將迴圈想成一個電腦版本的"往一個方向走 X 步，然後往另一個方向走 Y 步"的遊戲；作為範例，"往東走五步"可以用這個方法用迴圈表示：

```js
var step;
for (step = 0; step < 5; step++) {
  // 執行五次：從step為0到4
  console.log("Walking east one step");
}
```

有很多種不同種類的迴圈， 不過他們本質上都是做一樣的事：把一件動作重複地做一定的次數（而且也有可能做 0 次）。 各式各樣的迴圈機制提供了不同的方法來定義該迴圈的起始與結束。有些不同的情況下使用其中一種迴圈會比使用別種容易許多。

在 javaScript 中提供的迴圈陳述式分別為：

* for 陳述式
* do...while 陳述式
* while 陳述式
* label 陳述式
* break 陳述式
* continue 陳述式
* for...in 陳述式
* for...of 陳述式
`for` 陳述式
---------

一個for 迴圈不斷重複直到一個指定的條件式判斷為 false。JavaScript 的 for 迴圈跟 Java 還有 C 的 for 迴圈很相似。一個 for 陳述式看起來像下面這樣：
```

for ([初始表達式]; [條件式]; [遞增表達式])
  陳述式
```

當執行一個 for 迴圈時，會發生以下：

1. 如果有的話，初始表達式會被執行。這個表達式通常會初始化一或多個迴圈計數器，但是語法允許任何程度的複雜性。這個表達式也能用來宣告變數。
2. 條件式會被評估。如果評估出的值為 true，迴圈的敘事式便會執行。如果評估出的值為 false，這個 for 迴圈便會中止。如果條件式被省略了，狀態就會被假設是 true。
3. 執行敘事式。要執行多個敘事式時，使用區塊敘事式(`{ ... }`) 來把那些敘事式歸為一組。
4. 如果有更新表達式的遞增表達式便執行。然後 return 到第二步。
### 範例

以下的函式包含一個用來數在一個滾動列表中被選過的選項(a `<select>` (en-US) 允許複數選項的元素)的 for 陳述式 。這個 for 敘事式宣告了變數 i 並將其初始化為 0。 他檢查 i ，如果 i 少於在<select>元素中的選項數量，進行接著的 if 陳述式，並將 i 在每次通過迴圈後遞增。

```html
<form name="selectForm">
  <p>
    <label for="musicTypes"
 >Choose some music types, then click the button below:</label
 >
    <select id="musicTypes" name="musicTypes" multiple="multiple">
      <option selected="selected">R&B</option>
      <option>Jazz</option>
      <option>Blues</option>
      <option>New Age</option>
      <option>Classical</option>
      <option>Opera</option>
    </select>
  </p>
  <p><input id="btn" type="button" value="How many are selected?" /></p>
</form>

<script>
 function howMany(selectObject) {
 var numberSelected = 0;
 for (var i = 0; i < selectObject.options.length; i++) {
 if (selectObject.options[i].selected) {
 numberSelected++;
 }
 }
 return numberSelected;
 }

 var btn = document.getElementById("btn");
 btn.addEventListener("click", function () {
 alert(
 "Number of options selected: " + howMany(document.selectForm.musicTypes),
 );
 });
</script>
```

`do...while` 陳述式
----------------

`do...while` 陳述式會不斷重複直到一個特定的條件判斷為 false。一個 do...while 陳述式看起來像以下：
```

do
  陳述式
while (條件式);
```

`陳述式會在檢查條件式以前先執行一次。要執行多個陳述式的話，使用區塊陳述式來將那些陳述式歸為一組。如果條件式為true，那陳述式便再次執行。在每次執行的最後，條件會被檢查。當條件式為false時，` 停止執行並把控制傳給 `do...while接著的陳述式。`

### 範例

在下列範例中，do 迴圈重複了至少一次並不斷重複直到 i 不再比 5 少。

```js
var i = 0;
do {
  i += 1;
  console.log(i);
} while (i < 5);
```

`while` 陳述式
-----------

`while` 陳述式會不斷執行它的陳述式只要指定的條件式判斷為 true。一個 while 陳述式看起來如下：
```

while (condition)
  statement
```

如果條件式變成 false ，在迴圈中的陳述式會停止執行並控制交給跟在這個迴圈後面的陳述式。

條件式的測試發生於迴圈內的陳述式執行之前。如果條件式傳回 true ，陳述式便會被執行而判斷式會再被測試一次。如果條件式傳回 false ，停止執行並把控制交給跟在 while 迴圈後面的陳述式。

`要執行多個陳述式的話，使用區塊陳述式來將那些陳述式歸為一組。`

### 範例 1

以下的 while 迴圈在只要 n 比 3 少的情況下便會不斷重複：

```js
var n = 0;
var x = 0;
while (n < 3) {
  n++;
  x += n;
}
```

在每次的疊代，迴圈把 n 遞增並將其值加到 x 上。因此，x 跟 n 的值會是下列情況：

* 經過第一次迴圈後 `n` = 1 而 `x` = 1
* 經過第二次迴圈後 `n` = 2 而 `x` = 3
* 經過第三次迴圈後 `n` = 3 而 `x` = 6

在完成第三次迴圈後，判斷是 n<3 不再是 true ，所以迴圈終止。

### 範例 2

避免無限迴圈。確定在迴圈內的判斷式終究會變成 false； 不然迴圈會永遠不終止。在迴圈內的陳述式會永遠的執行因為判斷式永遠不會變成 false：

```js
while (true) {
  console.log("Hello, world");
}
```

`label` 陳述式
-----------

label 提供一個有識別字的陳述式讓你能在程式的別的地方參考。舉個例子，你能使用 label 來識別一個迴圈，然後使用 break 或 continue 陳述式來指示何時程式該中斷迴圈或是繼續他的執行。

label 陳述式的語法看起來如下：
```

label :
   statement
```

Label 的值可以是任何不是保留字的 JavaScript 識別字。你用 label 所識別的陳述式可以是任何陳述式。

### 範例

在這個範例，`markLoop這個label 識別一個while 迴圈。`

```js
markLoop: while (theMark == true) {
  doSomething();
}
```

`break` 陳述式
-----------

`break` 陳述式是用來終止一個迴圈，一個 switch 多重控制選項，或是和一個 label 陳述式聯合使用。

* 當你在沒有 label 的情況下使用 break，它會馬上終止最內部的 while , do-while , for ,或是 switch 區間並將控制交給接下來的陳述式。
* 當你跟 label 一起使用的時候，它會終止那個特定的被 label 的陳述式。

break 陳述式的語法看起來如下：

1. `break;`
2. `break label;`

第一種語法會終止最內部的迴圈或 switch 區間；第二種語法會終止那個特定的 label 陳述式。

### 範例 1

以下的範例會不斷重複跑迴圈直到有在陣列裡的元素符合 theValue 的值：

```js
for (var i = 0; i < a.length; i++) {
  if (a[i] == theValue) {
    break;
  }
}
```

### 範例 2：Break 至一個 label 陳述式

```js
var x = 0;
var z = 0;
labelCancelLoops: while (true) {
  console.log("Outer loops: " + x);
  x += 1;
  z = 1;
  while (true) {
    console.log("Inner loops: " + z);
    z += 1;
    if (z === 10 && x === 10) {
      break labelCancelLoops;
    } else if (z === 10) {
      break;
    }
  }
}
```

`continue` 陳述式
--------------

`continue` 陳述式可以用於重新開始一個 while , do-while, for, 或 label 陳述式。

* 當你在沒有 label 的情況下使用 continue，它會終止現在最內部 while, do-while , for 陳述式區間的迭代並繼續執行該迴圈的下一個迭代。跟 break 陳述式不同的是，continue 不會把整個迴圈的執行給終止。在 while 迴圈中，它會跳回條件式的判斷。在 for 迴圈中，它會跳至遞增陳述式。
* 當 contunue 跟 label 一起使用的時候，它會應用至被 label 識別的那個迴圈陳述式。

continue 陳述式的語法看起來如下：

1. `continue;`
2. `continue`*`label;`*
### 範例 1

以下的範例有 while 迴圈以及一個在 i 的值為 3 的時候執行的 continue 陳述式。因此，n 的值會連著是 1, 3, 7, 12。

```js
var i = 0;
var n = 0;
while (i < 5) {
  i++;
  if (i == 3) {
    continue;
  }
  n += i;
}
```

### 範例 2

一個被 label 成 checkiandj 的陳述式包還著一個被 label 成 checkj 的陳述式。如果遇到了 continue，程式會終止現在的這輪迴圈並開始下一輪。每次遇到 continue，checkj 就會一直重複直到它的條件式返回 false。當 false 被傳回時，checkiandj 陳述式剩下的陳述式已被完成，而 checkiandj 也會繼續重複直到它的條件式傳回 false。當 false 被傳回，程式會繼續進行接著 checkiandj 後面的陳述式。

如果 continue 有了 checkiandj 的 label 程式會從 checkiandj 陳述式的頭開始繼續。

```js
checkiandj: while (i < 4) {
  console.log(i);
  i += 1;
  checkj: while (j > 4) {
    console.log(j);
    j -= 1;
    if (j % 2 == 0) {
      continue checkj;
    }
    console.log(j + " is odd.");
  }
  console.log("i = " + i);
  console.log("j = " + j);
}
```

`for...in` 陳述式
--------------

`for...in` 陳述式重複一個指定的變數來循環一個物件所有可枚舉的屬性。至於每個獨特的屬性，JavaScript 執行特定的陳述式。一個`for...in` 陳述式看起來像以下：
```

for (variable in object) {
  statements
}
```

### 範例

以下的函式透過它的參數得到一個物件和物件的名字。接著它循環這個物件的所有屬性並傳回一個列出屬性名和值的字串。

```js
function dump\_props(obj, obj\_name) {
  var result = "";
  for (var i in obj) {
    result += obj_name + "." + i + " = " + obj[i] + "<br>";
  }
  result += "<hr>";
  return result;
}
```

對於一個擁有 make 跟 model 屬性的物件 car 來說，執行結果是：

```js
car.make = Ford;
car.model = Mustang;
```

### 陣列

雖然用 for...in 來迭代 `Array` 元素很吸引人，但是它傳回的除了數字的索引之外還有可能是你自己定的屬性名。因此還是用帶有數字索引的傳統`for迴圈來迭帶一個陣列會比較好。因為如果你想改變陣列物件，比如增加屬性或是方法，`**for...in** 陳述式迭代的是自定的屬性而不是陣列的元素。

`for...of` 陳述式
--------------

`for...of` (en-US) 陳述式在iterable objects (en-US)(可迭代的物件)上建立了一個循環 (包含 `Array`, `Map`, `Set`, arguments (en-US)(參數) 物件 等等), 對每個獨特屬性的值使用一個準備被執行的有陳述式的自訂迭代掛勾。
```

for (variable of object) {
  statement
}
```

下列的範例可看出`for...of` 迴圈跟 `for...in` 迴圈的差別。 `for...in` 在屬性名字循環，而`for...of` 在屬性的值循環。

```js
let arr = [3, 5, 7];
arr.foo = "hello";

for (let i in arr) {
  console.log(i); // logs "0", "1", "2", "foo"
}

for (let i of arr) {
  console.log(i); // logs 3, 5, 7
}
```

* « 前頁
* 次頁 »


JavaScript 概觀 - JavaScript

JavaScript 概觀
=============

* « 前頁
* 次頁 »

這個章節的內容主要是介紹 JavaScript 和討論一些 JavaScript 的基本概念。

在開始前需具備之能力
----------

本手冊假設你具有以下基本背景：

* 對網際網路及全球資訊網有大致上的了解
* 對 HTML(HyperText Markup Language )語法理解至一定程度
* 有寫過程式的經驗，若你是完全的新手，可嘗試在主頁上有關JavaScript的教程。
什麼是 JavaScript？
---------------

JavaScript 是個跨平台、物件導向、輕小型的腳本語言。作為獨立語言並不實用，而是為了能簡單嵌入其他產品和應用程式（例如：網頁瀏覽器）而設計。JavaScript 若寄宿在主體環境（Host environment）時，可以與環境中的物件 （Object）相連，並以程式控制這些物件。

Core JavaScript 包含了物件的核心集合（例如: `Array、` `Date、` `Math`）及語言成份的核心集合（例如：運算子、控制結構、敘述）。在 Core JavaScript 增加額外的物件即可擴充各式各樣的功能，例如：

* 用戶端 - JavaScript 藉由提供物件來擴增核心語言達到控制網頁瀏覽器和其文件物件模型（DOM，Document Object Model）的目的。

舉例來說：用戶端的擴充套件允許某個應用程式將元素放置在 HTML 的表單上及對使用者的操作（例如：滑鼠點選、表單輸入、頁面導覽等）做出回應。

* 伺服器端 - JavaScript 藉由提供和伺服器上執行 JavaScript 相關的物件來擴增核心語言。

舉例來說：伺服器端的擴充套件允許某個應用程式和相關的資料庫交換訊息、對一個其他應用程式的呼叫提供連續性的資訊、在伺服器上執行檔案操作。

透過 JavaScript 的 LiveConnect 功能，你可以使 Java 和 JavaScript 的程式碼彼此相連。在 JavaScript 的程式碼中，你可以實例化（instantiate）Java 的物件並存取那些物件的公有方法（public methods）及欄位（fields）。在 Java 的程式碼中，你可以存取 JavaScript 的物件、屬性（properties）及方法（methods）。

Netscape 公司發明了 JavaScript ，而 JavaScript 的第一次使用正是在 Netscape 自家的瀏覽器上。

JavaScript 與 Java
-----------------

JavaScript 與 Java 在某些方面非常相似但本質上卻是不同的。 JavaScript 雖然和 Java 類似，卻沒有 Java 的靜態定型（static typing）及強型態確認（strong type checking）特性。 JavaScript 遵從大部份的 Java 表達式語法、命名傳統和基本的流程控制概念，這特性同時也是為何要將 LiveScript 重新命名為 JavaScript 的原因。

相較於 Java 由許多類別中的宣告建立的 compile-time 系統，JavaScript 支援一個由少數代表數值（numeric）、布林值（Boolean）、字串（string）的資料類型所構成的執行期函式庫（runtime system）。JavaScript 擁有一個基於原型的物件模型（prototype-based object model）而不是普遍使用的基於類別的物件模型（class-based object model）。基於原型的物件模型提供動態繼承（dynamic inheritance）的功能，意即被繼承的物件可以根據個別的物件而改變。JavaScript 也支援不需任何特殊宣告的函式，函式可以是物件的屬性，如同鬆散型態方法（loosely typed method）那樣執行。

JavaScript 和 Java 相比起來，算是一個格式非常自由的語言。你不需要宣告所有的變數、類別（class）、方法，你不需要注意哪些方法是公有（public）或私有（private）或受保護的（protected），你不需要實作介面（interface）。變數、參數及函式回傳的型態並不是顯性型態。

Java 是一個為了快速執行與安全型態而設計的基於類別的程式語言（class-based programming language）。安全型態意即你在 Java 中無法將整數丟給一個物件參考，也無法藉由中斷 Java bytecodes 來存取私有的記憶體。 Java 的基於類別模型（class-based model）意思是程式由專門的類別及其方法所組成。Java 的類別繼承（class inheritance）和強型別（strong typing）通常需要嚴謹的耦合對象階級（coupled object hierarchies）。這些需求使得 Java 在程式的撰寫上比 JavaScript 來的複雜。

相反的，JavaScript 承襲了如同 HyperTalk 和 dBASE 相同的精神：較小、動態類型。 這些腳本語言因為較簡單的語法、特殊化的功能、較寬鬆的要求等特性，進而提供了許多軟體開發工具（programming tool）給更多更廣大的愛好者。

表 1.1 - JavaScript 和 Java 比較

| JavaScript | Java |
| --- | --- |
| 物件導向。物件的型態之間無區別。藉由原型機制（prototype mechanism）和屬性（properties）實行繼承。屬性和方法可被動態新增至任何物件。 | 類別導向。物件藉由類別階級（class hierarchy）而被分離至類別和所有繼承的實體（instance）中。類別和實體無法動態新增屬性和方法。 |
| 變數資料型態沒有宣告就可使用（動態定型，dynamic typing）。 | 變數資料型態必須宣告才可使用（靜態定型，static typing）。 |
| 無法自動覆寫到硬碟。 | 無法自動覆寫到硬碟。 |

更多關於 JavaScript 和 Java 的差異比較，請參見 Details of the Object Model 。

JavaScript 與 ECMAScript 規格
--------------------------

Netscape 公司發明了 JavaScript ，而 JavaScript 的第一次應用正是在 Netscape 瀏覽器。然而，Netscape 後來和 Ecma International（一個致力於將資訊及通訊系統標準化的歐洲組織，前身為 ECMA - 歐洲計算機製造商協會）合作，開發一個基於 JavaScript 核心並同時兼具標準化與國際化的程式語言，這個經過標準化的 JavaScript 便稱作 ECMAScript ，和 JavaScript 有著相同的應用方式並支援相關標準。各個公司都可以使用這個開放的標準語言去開發 JavaScript 的專案。ECMAScript 標準記載於 ECMA-262 這個規格中。

ECMA-262 標準同時也經過 ISO（國際標準化組織）認証，成為 ISO-16262 標準。你可以在 Mozilla 的網站上找到 PDF 版本的 ECMA-262，但這板本已過期；你也可以在 Ecma International 的網站 找到這個規格。 ECMAScript 規格中並沒有描述已經被 W3C（全球資訊網協會）標準化的文件物件模型（DOM）。文件物件模型定義了 HTML 文件物件（document objects）和腳本之間運作的方式。

### JavaScript 版本與 ECMAScript 版本之間的關係

ECMAScript 規格（ECMA-262）在 Netscape 和 Ecma International 的密切合作下產生。下表描述了 JavaScript 的版本和 ECMAScript 的版本之間的關係。

表 1.2 - JavaScript 版本及 ECMAScript 版本

| JavaScript 的版本 | 和 ECMAScript 版本的關係 |
| --- | --- |
| JavaScript 1.1 | ECMA-262 第1版是基於 JavaScript 1.1 建立的。 |
| JavaScript 1.2 |
 ECMA-262 在 JavaScript 1.2 發行之際尚未完成。以下是 JavaScript 1.2
 並未完全相容於 ECMA-262 第1版的原因：
 * Netscape 在 JavaScript 1.2 中新增了一些特性，而這些特性在 ECMA-262
 並未被考慮到。
* ECMA-262 新增了兩個新的特性：使用國際化的 Unicode
 編碼及統一了不同平台之間的行為。JavaScript 1.2
 中的一些特性，例如：日期物件（Date
 object）是具有平台依賴性（platform-dependent）並且使用平台特製化行為（platform-specific
 behavior）的。
 |
| JavaScript 1.3 | JavaScript 1.3 完全相容於 ECMA-262 第1版。JavaScript 1.3 藉由保留所有在 JavaScript 1.2 新增的特性（除了 == 和 ！=
 以外，因為要和 ECMA-262 一致），解決了 JavaScript 1.2 和 ECMA-262
 之間的衝突。
  |
| JavaScript 1.4 | JavaScript 1.4 完全相容於 ECMA-262 第1版。ECMAScript 第3版規格在 JavaScript 1.4 發行之際尚未完成。
  |
| JavaScript 1.5 | JavaScript 1.5 完全相容於 ECMA-262 第3版。 |

**備註：** ECMA-262 第 2 版是由已修正錯誤的第 1 版並加上些微的更動構成。現今由 Ecma International 的 TC39 工作組（TC39 Working Group）所發行的版本是 ECMAScript 5.1 版

JavaScript Reference (en-US) 指出了哪些 JavaScript 的特性是相容於 ECMAScript 的。

JavaScript 永遠包含許多非 ECMAScript 規格中的特性；

JavaScript 不僅相容於 ECMAScript 更提供了額外的特性。

### JavaScript 使用說明 v.s ECMAScript 規格

ECMAScript 規格是執行 ECMAScript 所必須的條件，當你想判斷某個 JavaScript 的特性是否在其他 ECMAScript 實作中有被支援時，ECMAScript 規格是非常有用的。如果你打算撰寫 JavaScript 程式碼並在程式碼中使用僅有 ECMAScript 所支援的特性，那你可能需要查閱一下 ECMAScript 規格。

ECMAScript 文件並不是為了幫助腳本程式設計師而撰寫，如果想知道撰寫腳本的相關資訊，請參考 JavaScript 使用說明。

### JavaScript 和 ECMAScript 的專門術語

ECMAScript 規格使用的術語和語法對於 JavaScript 的程式設計師來說可能不是那麼的親切。雖然語言的描述在 ECMAScript 中可能會有所不同，但語言本身的性質仍然是不變的。JavaScript 支援所有在 ECMAScript 規格中被描述到的功能。

JavaScript 使用說明對於語言的觀點的描述較適合 JavaScript 的程式設計師。例如：

* 因為全域物件（Global Object）並不會被直接使用，所以並沒有在 JavaScript 文件說明中被論及。而像是全域物件的方法和屬性這些有被使用到的，就有在 JavaScript 使用說明中被論及，但被稱作頂層（top-level）函式和頂層屬性。
* 無參數建構函式（no parameter constructor，也稱作零參數建構函式，zero-argument constructor）。帶有 Number 物件及 String 物件的無參數建構函式並沒有在 JavaScript 使用說明中被論及，因為其產生出來的東西用途有限：一個沒有參數的 Number 建構函式回傳 +0 、一個沒有參數的 String 建構函式回傳 "" （一個空字串）


JavaScript 型別陣列 - JavaScript

JavaScript 型別陣列
===============

* « 前頁
* 次頁 » (en-US)

當 Webapp 有了視頻、音頻操作，及用 WebSocket 存取原始資料等等的功能而變得越來越強大，讓 JavaScript 代碼可以快速、簡單地操作原始二進制資料的好處就越來越明顯。以前唯一的解法是視原始資料為字串並用 `charCodeAt()` (en-US) 方法讀取資料緩衝的位元組。

然而，由於需要多次型別轉換（特別是二進制資料並非以位元組計算，如 32 位元整數或浮點數），這個解法既慢又容易發生錯誤。

JavaScript 型別陣列提供了存取二進制資料更有效率的機制。

型別陣列不該與一般的陣列搞混，當對型別陣列呼叫`Array.isArray()`時會回傳`false`。此外，一般陣列所提供的方法並非全部被型別陣列所支援(如 push 以及 pop 方法)

緩衝與視圖：型別陣列的架構
-------------

為了追求最大的可朔性與效率，JavaScript 型別陣列的實作分為**緩衝**與**視圖**。一個緩衝（以類別 `ArrayBuffer` 實作）為代表一塊資料資料的物件，它沒有任何格式，也沒有任何存取其內容的機制。想存取一個緩衝所佔的記憶體必須用一個視圖。一個視圖提供了一種前後關係 — 資料型別、起始偏移與元素的數目 — 使得資料變成真實的型別陣列。視圖以類別 `ArrayBufferView` 與其子類別實作。

![Typed arrays in an ArrayBuffer](/zh-TW/docs/Web/JavaScript/Guide/Typed_arrays/typed_arrays.png)

### ArrayBuffer

`ArrayBuffer` 是一種資料型態，用於表示通用的固定長度二進制資料緩衝區。 你不能直接操作 `ArrayBuffer` 的內容。但是，你可以建立一個型別陣列視圖 (typed array view) 或一個 `DataView` (en-US)，它以特定格式代表緩衝區，並使用它讀取和寫入緩衝區的內容。

### 型別陣列視圖 (Typed array views)

型別陣列視圖具有自述性名稱，並為所有常用的數字類型（如 `Int8`, `Uint32`, `Float64` 等）提供視圖。 有一個特殊的型別陣列視圖 `Uint8ClampedArray`。 它的範圍值在 0 到 255 之間。它對於 Canvas 的資料處理非常有用。

| Type | Value Range | Size in bytes | Description | Web IDL type | Equivalent C type |
| --- | --- | --- | --- | --- | --- |
| `Int8Array` (en-US) | -128 to 127 | 1 | 8-bit two's complement signed integer | `byte` | `int8_t` |
| `Uint8Array` (en-US) | 0 to 255 | 1 | 8-bit unsigned integer | `octet` | `uint8_t` |
| `Uint8ClampedArray` (en-US) | 0 to 255 | 1 | 8-bit unsigned integer (clamped) | `octet` | `uint8_t` |
| `Int16Array` (en-US) | -32768 to 32767 | 2 | 16-bit two's complement signed integer | `short` | `int16_t` |
| `Uint16Array` (en-US) | 0 to 65535 | 2 | 16-bit unsigned integer | `unsigned short` | `uint16_t` |
| `Int32Array` (en-US) | -2147483648 to 2147483647 | 4 | 32-bit two's complement signed integer | `long` | `int32_t` |
| `Uint32Array` (en-US) | 0 to 4294967295 | 4 | 32-bit unsigned integer | `unsigned long` | `uint32_t` |
| `Float32Array` (en-US) | 1.2x10^-38 to 3.4x10^38 | 4 | 32-bit IEEE floating point number ( 7 significant digits e.g. 1.1234567) | `unrestricted float` | `float` |
| `Float64Array` (en-US) | 5.0x10^-324 to 1.8x10^308 | 8 | 64-bit IEEE floating point number (16 significant digits e.g. 1.123...15) | `unrestricted double` | `double` |

### DataView

The `DataView` (en-US) is a low-level interface that provides a getter/setter API to read and write arbitrary data to the buffer. This is useful when dealing with different types of data, for example. Typed array views are in the native byte-order (see Endianness (en-US)) of your platform. With a `DataView` you are able to control the byte-order. It is big-endian by default and can be set to little-endian in the getter/setter methods.

Web APIs using typed arrays
---------------------------

`FileReader.prototype.readAsArrayBuffer()`

The `FileReader.prototype.readAsArrayBuffer()` method starts reading the contents of the specified `Blob` or `File`.

`XMLHttpRequest.prototype.send()`

`XMLHttpRequest` instances' `send()` method now supports typed arrays and `ArrayBuffer` objects as argument.

`ImageData.data` (en-US)

Is a `Uint8ClampedArray` (en-US) representing a one-dimensional array containing the data in the RGBA order, with integer values between `0` and `255` inclusive.

範例
--

### 使用視圖與緩衝

先來建立一個 16 位元組的緩衝：

```js
var buffer = new ArrayBuffer(16);
```

在這個時候，我們有位元組全部初始為 0 的一塊記憶體，但是用它做不了什麼事。我們只能確認它的確是 16 的位元組的長度：

```js
if (buffer.byteLength === 16) {
  console.log("沒錯，是 16 個位元組。");
} else {
  console.log("糟糕，長度不對！");
}
```

要用這個緩衝搞點花樣之前，建立一個視圖是必需的。來建立一個視圖把這個緩衝當作 32 位元的有符號整數：

```js
var int32View = new Int32Array(buffer);
```

這樣就可以把它當作一般的陣列以存取欄位：

```js
for (var i = 0; i < int32View.length; i++) {
  int32View[i] = i \* 2;
}
```

這會把此陣列的四個欄位以 0、2、4、6 填滿（四個 4 位元組，總共 16 位元組）。

### 同一份資料的多個視圖

考慮在同一份資料上建立多個視圖的有趣情形。舉例來說，繼續使用上面的代碼：

```js
var int16View = new Int16Array(buffer);

for (var i = 0; i < int16View.length; i++) {
  console.log("Entry " + i + ": " + int16View[i]);
}
```

雖然該緩衝上已有一個 32 位元的視圖，這裡建立了同一個緩衝上的 16 位元整數視圖，這裡的輸出為 0, 0, 2, 0, 4, 0, 6, 0。

繼續考慮這種情況：

```js
int16View[0] = 32;
console.log("現在32位元陣列的欄位0是" + int32View[0]);
```

輸出為"現在 32 位元陣列的欄位 0 是 32"。也就是，這兩個陣列真的是同一個資料緩衝的在不同格式下的看法。其他 view types 也是同樣的情形。

### 處理複雜的資料結構

在單一個緩衝使用不同型別、不同起始偏移的多個視圖以操作資料物件含有的多個資料型別。這個方法可以用在使用 WebGL 、資料檔案、js-ctypes 時遇到的複雜的資料結構。

考慮這個 C 結構：

```cpp
struct someStruct {
  unsigned long id;
  char username[16];
  float amountDue;
};
```

可以用以下方法存取含有這種資料格式的緩衝：

```js
var buffer = new ArrayBuffer(24);

// ... 將資料讀入緩衝 ...

var idView = new Uint32Array(buffer, 0, 1);
var usernameView = new Uint8Array(buffer, 4, 16);
var amountDueView = new Float32Array(buffer, 20, 1);
```

舉例來說，可以用 `amountDueView[0]` 存取 amountDue。

**備註：** C 結構的 data structure alignment 是與使用平台有關，須小心這些填充上的差異。

### Conversion to normal arrays

After processing a typed array, it is sometimes useful to convert it back to a normal array in order to benefit from the `Array` prototype. This can be done using `Array.from`, or using the following code where `Array.from` is unsupported.

```js
var typedArray = new Uint8Array([1, 2, 3, 4]),
  normalArray = Array.prototype.slice.call(typedArray);
normalArray.length === 4;
normalArray.constructor === Array;
```

參見
--

* Getting `ArrayBuffer`s or typed arrays from *Base64*-encoded strings (en-US)
* `StringView` – a C-like representation of strings based on typed arrays
* Faster Canvas Pixel Manipulation with Typed Arrays
* Typed Arrays: Binary Data in the Browser
* Endianness (en-US)


數字與日期 - JavaScript

數字與日期
=====

* « 前頁
* 次頁 » (en-US)

這個章節將介紹如何在 JavaScript 中處理數字與日期。

數字(Numbers)
-----------

在 JavaScript 中, Number 所使用的標準依照 double-precision 64-bit binary format IEEE 754 (i.e. number 的區間是 -(2^53 -1) 到 2^53 -1)。**整數是沒有特定的類型**。

此外還可以顯示浮點數，三種符號數值: `+``Infinity`， `-``Infinity`， and `NaN` (not-a-number)。

`BigInt` 是 Javascript 最新的功能，它可以表示一個很大的整數。使用 `BigInt需要注意一點`，`BigInt` 和`Number`不能在同一個 operation 混用還有當用 `Math` 物件時不能使用`BigInt`。

請參照 JavaScript data types and structures 來取得更多詳細資料。

你可以用四種進制表示數字：十進制 (decimal)，二進制 (binary)，八進制 (octal) 以及十六進制 (hexadecimal)。

### 十進制數值

```js
1234567890;
42;

// 以零為開頭時要小心：

0888; // 888 解析為 十進制數值
0777; // 在 non-strict 模式下將解析成八進制 (等同於十進制的 511)
```

請注意，十進位數字允許第一個數字設為零(`0`)的話，前提是後面接的數字必須要有一個數字大於 8(例如輸入 0888 結果會是 888，輸入 068 結果會是 68)，不然則會被轉成８進位(例如 0777 結果會是 511，輸入 063 結果會是 51)。

### 二進制數值

二進制數值以 0 為開頭並跟著一個大寫或小寫的英文字母 「B」 (`0b` 或 `0B`)。如果 `0b` 後面接著的數字不是 0 或 1，那會丟出 `SyntaxError(語法錯誤)` (en-US): "Missing binary digits after 0b"。

```js
var FLT\_SIGNBIT = 0b10000000000000000000000000000000; // 2147483648
var FLT\_EXPONENT = 0b01111111100000000000000000000000; // 2139095040
var FLT\_MANTISSA = 0b00000000011111111111111111111111; // 8388607
```

### 八進制數值

八進制數值以 0 為開頭。如果 `0` 後面的數字超出 0 到 7 這個範圍，將會被解析成十進制數值。

```js
var n = 0755; // 493
var m = 0644; // 420
```

Strict mode in ECMAScript 5 forbids octal syntax. Octal syntax isn't part of ECMAScript 5, but it's supported in all browsers by prefixing the octal number with a zero: `0644 === 420` and`"\045" === "%"`. In ECMAScript 2015, octal numbers are supported if they are prefixed with `0o`, e.g.:

```js
var a = 0o10; // ES2015: 8
```

### 十六進制數值

十六進制數值以 0 為開頭並跟著一個大寫或小寫的英文字母 「X」(`0x` 或 `0X`)。如果 `0b` 後面接著的值超出範圍 (0123456789ABCDEF)，那會丟出 `SyntaxError(語法錯誤)` (en-US):"Identifier starts immediately after numeric literal"。

```js
0xfffffffffffffffff; // 295147905179352830000
0x123456789abcdef; // 81985529216486900
0xa; // 10
```

### 指數運算

```js
1e3; // 1000
2e6; // 2000000
0.1e2; // 10
```

`Number` 物件
-----------

The built-in `Number` object has properties for numerical constants, such as maximum value, not-a-number, and infinity. You cannot change the values of these properties and you use them as follows:

```js
var biggestNum = Number.MAX\_VALUE;
var smallestNum = Number.MIN\_VALUE;
var infiniteNum = Number.POSITIVE\_INFINITY;
var negInfiniteNum = Number.NEGATIVE\_INFINITY;
var notANum = Number.NaN;
```

You always refer to a property of the predefined `Number` object as shown above, and not as a property of a `Number` object you create yourself.

下面這張表格整理了 `Number` 物件的屬性

`Number` **的屬性**

| 屬性 | 描述 |
| --- | --- |
| `Number.MAX_VALUE` (en-US) | 可表示的最大數值 |
| `Number.MIN_VALUE` (en-US) | 可表示的最小數值 |
| `Number.NaN` (en-US) | 表示「非數值」（Not-A-Number）的數值 |
| `Number.NEGATIVE_INFINITY` (en-US) | Special negative infinite value; returned on overflow |
| `Number.POSITIVE_INFINITY` (en-US) | Special positive infinite value; returned on overflow |
| `Number.EPSILON` (en-US) | Difference between one and the smallest value greater than one that can be represented as a `Number`. |
| `Number.MIN_SAFE_INTEGER` (en-US) | 可以在 JavaScript 中安全表示的最小數值。 |
| `Number.MAX_SAFE_INTEGER` (en-US) | 可以在 JavaScript 中安全表示的最大數值。 |

| 方法 | 描述 |
| --- | --- |
| `Number.parseFloat()` (en-US) | 字串轉換成浮點數。 等同於全域函式 `parseFloat()` (en-US) 。 |
| `Number.parseInt()` (en-US) | 以指定的基數將字串轉換成整數。 等同於全域函式 `parseInt()` 。 |
| `Number.isFinite()` (en-US) | 判定給定的值是不是一個有限數。 |
| `Number.isInteger()` (en-US) | 判定給定的值是不是一個整數 |
| `Number.isNaN()` (en-US) | Determines whether the passed value is `NaN`. More robust version of the original global `isNaN()`. |
| `Number.isSafeInteger()` (en-US) | Determines whether the provided value is a number that is a *safe integer*. |

The `Number` prototype provides methods for retrieving information from `Number` objects in various formats. The following table summarizes the methods of `Number.prototype`.

| 方法 | 描述 |
| --- | --- |
| `toExponential()` | Returns a string representing the number in exponential notation. |
| `toFixed()` | Returns a string representing the number in fixed-point notation. |
| `toPrecision()` (en-US) | Returns a string representing the number to a specified precision in fixed-point notation. |

`Math` 物件
---------

The built-in `Math` object has properties and methods for mathematical constants and functions. For example, the `Math` object's `PI` property has the value of pi (3.141...), which you would use in an application as

```js
Math.PI;
```

Similarly, standard mathematical functions are methods of `Math`. These include trigonometric, logarithmic, exponential, and other functions. For example, if you want to use the trigonometric function sine, you would write

```js
Math.sin(1.56);
```

Note that all trigonometric methods of `Math` take arguments in radians.

The following table summarizes the `Math` object's methods.

| 方法 | 描述 |
| --- | --- |
| `abs()` (en-US) | 絕對值 |
| `sin()` (en-US), `cos()` (en-US), `tan()` (en-US) | 三角函數; 引數以弳度表示 |
| `asin()` (en-US), `acos()` (en-US), `atan()` (en-US), `atan2()` (en-US) | 反三角函數; 回傳值以弳度表示 |
| `sinh()` (en-US), `cosh()` (en-US), `tanh()` (en-US) | 雙曲函數; 引數以 hyperbolic angle 表示 |
| `asinh()` (en-US), `acosh()` (en-US), `atanh()` (en-US) | 反雙曲函數; 回傳值以 hyperbolic angle 表示 |
| `pow()` (en-US), `exp()` (en-US), `expm1()` (en-US), `log10()` (en-US), `log1p()` (en-US), `log2()` (en-US) | 指數及對數函式 |
| `floor()`, `ceil()` | 回傳小於等於/大於等於指定數字的最大/最小整數 |
| `min()` (en-US), `max()` (en-US) | Returns lesser or greater (respectively) of comma separated list of numbers arguments |
| `random()` | 回傳一個介於 0 到 1 之間的數值 |
| `round()`, `fround()` (en-US), `trunc()` (en-US), | Rounding and truncation functions. |
| `sqrt()` (en-US), `cbrt()` (en-US), `hypot()` (en-US) | Square root, cube root, Square root of the sum of square arguments. |
| `sign()` (en-US) | The sign of a number, indicating whether the number is positive, negative or zero. |
| `clz32()` (en-US), `imul()` (en-US) | Number of leading zero bits in the 32-bit binary representation. The result of the C-like 32-bit multiplication of the two arguments. |

Unlike many other objects, you never create a `Math` object of your own. You always use the built-in `Math` object.

`Date` 物件
---------

JavaScript 沒有所謂日期(date)的數據型態(data type)。你可以使用 `Date` 物件及其方法去設定日期跟時間來滿足你的需求 。`Date` 物件有大量的設定取得操作日期的方法(method)，但它沒有屬性。

JavaScript 處理日期的方式跟 Java 類似。這兩個語言有許多一樣的 date 方法，且都將日期儲存為從 1970 年 1 月 1 號 0 時 0 分 0 秒以來的毫秒數(millisecond)。

`Date` 物件範圍是 -100,000,000 days to 100,000,000 days 以 1970 年 1 月 1 號 0 時 0 分 0 秒 UTC 為基準。

創建一個`Date`物件:

```js
var dateObjectName = new Date([parameters]);
```

在這裡創建一個名為`dateObjectName` 的 `Date` 物件；它可以是一個新的物件或是某個以存在的物件當中的屬性。

Calling `Date` without the `new` keyword returns a string representing the current date and time.

The `parameters` in the preceding syntax can be any of the following:

* Nothing: creates today's date and time. For example, `today = new Date();`.
* A string representing a date in the following form: "Month day, year hours:minutes:seconds." For example, `var Xmas95 = new Date("December 25, 1995 13:30:00")`. If you omit hours, minutes, or seconds, the value will be set to zero.
* A set of integer values for year, month, and day. For example, `var Xmas95 = new Date(1995, 11, 25)`.
* A set of integer values for year, month, day, hour, minute, and seconds. For example, `var Xmas95 = new Date(1995, 11, 25, 9, 30, 0);`.
### `Date` 的方法

The `Date` object methods for handling dates and times fall into these broad categories:

* "set" methods, for setting date and time values in `Date` objects.
* "get" methods, for getting date and time values from `Date` objects.
* "to" methods, for returning string values from `Date` objects.
* parse and UTC methods, for parsing `Date` strings.

With the "get" and "set" methods you can get and set seconds, minutes, hours, day of the month, day of the week, months, and years separately. There is a `getDay` method that returns the day of the week, but no corresponding `setDay` method, because the day of the week is set automatically. These methods use integers to represent these values as follows:

* Seconds and minutes: 0 到 59
* Hours: 0 到 23
* Day: 0 (星期日) 到 6 (星期六)
* Date: 1 到 31 (這個月的第幾天)
* Months: 0 (一月) 到 11 (十二月)
* Year: years since 1900

舉例，假設你定義了一個日期如下：

```js
var Xmas95 = new Date("December 25, 1995");
```

那 `Xmas95.getMonth()` 將會回傳 11， `Xmas95.getFullYear()` 會回傳 1995。

`getTime` 及 `setTime` 這兩個方法對於比較日期有幫助。 The `getTime` method returns the number of milliseconds since January 1, 1970, 00:00:00 for a `Date` object.

For example, the following code displays the number of days left in the current year:

```js
var today = new Date();
var endYear = new Date(1995, 11, 31, 23, 59, 59, 999); // Set day and month
endYear.setFullYear(today.getFullYear()); // Set year to this year
var msPerDay = 24 \* 60 \* 60 \* 1000; // Number of milliseconds per day
var daysLeft = (endYear.getTime() - today.getTime()) / msPerDay;
var daysLeft = Math.round(daysLeft); //returns days left in the year
```

This example creates a `Date` object named `today` that contains today's date. It then creates a `Date` object named `endYear` and sets the year to the current year. Then, using the number of milliseconds per day, it computes the number of days between `today` and `endYear`, using `getTime` and rounding to a whole number of days.

The `parse` method is useful for assigning values from date strings to existing `Date` objects. For example, the following code uses `parse` and `setTime` to assign a date value to the `IPOdate` object:

```js
var IPOdate = new Date();
IPOdate.setTime(Date.parse("Aug 9, 1995"));
```

### 範例

下面這個範例，`JSClock()` 這個函式將會以數位時鐘的格式回傳時間。

```js
function JSClock() {
  var time = new Date();
  var hour = time.getHours();
  var minute = time.getMinutes();
  var second = time.getSeconds();
  var temp = "" + (hour > 12 ? hour - 12 : hour);
  if (hour == 0) temp = "12";
  temp += (minute < 10 ? ":0" : ":") + minute;
  temp += (second < 10 ? ":0" : ":") + second;
  temp += hour >= 12 ? " P.M." : " A.M.";
  return temp;
}
```

`JSClock` 這個函式會先建立一個名為 `time` 的 `Date` 物件; 因為沒有提供任何引數，所以會放入目前的日期及時間。接著呼叫 `getHours` 、 `getMinutes` 以及 `getSeconds` 這三個方法將現在的時、分以及秒分別指定給 `hour` 、 `minute` 以及 `second` 這三個變數。

接著的四行指令將會建立一個時間的字串。第一行的指令建立了一個變數 `temp`，以條件運算式指定值; 如果 `hour` 大於 12，那就指定 (`hour - 12`)，不然會直接指定 `hour`， 但如果 `hour` 等於 0 ， 則改為 12。

接著下一行將 `minute` 加到 `temp` 中。如果 `minute` 小於 10, 則會在附加時補上一個零; 不然的話會直接加上冒號及分鐘數。秒數也是以同樣的作法附加到 `temp` 上。

最後，判斷 `hour` 是不是大於等於 12 ，如果是就在 `temp` 加上 "P.M." ，不然就加上 "A.M."。

* « 前頁
* 次頁 » (en-US)


流程控制與例外處理 - JavaScript

流程控制與例外處理
=========

* « 前頁
* 次頁 »

JavaScript 擁有許多陳述式，特別是流程控制的陳述式，你可以用這些陳述式來增加程式的互動性。這個章節將會概要介紹陳述式。

JavaScript 參考 (en-US)中有比本章更多關於陳述式的細節。 在 Javascript 程式碼中，分號（;）被用來隔開陳述式。

任何 JavaScript 運算式也是一個陳述式。 有關運算式的完整資訊，請參閱運算式與運算子。

區塊陳述式
-----

最基本的陳述式是「用於將陳述式分塊」的「區塊陳述式」。程式區塊被以一對大括號隔離開來：
```

{
  陳述式 1;
  陳述式 2;
  .
  .
  .
  陳述式 n;
}
```

### 範例

區塊陳述式經常與流程控制陳述式（例如：`if`、`for`、`while`）搭配使用。

```js
while (x < 10) {
  x++;
}
```

在這裏，`{ x++; }` 是區塊陳述式。

**重要**：JavaScript 在 ECMAScript2015 （第六版）以前的版本並**沒有**區塊範圍的概念。 在區塊中的變數有效範圍是包含該區塊的函式或者是執行檔，並且在區塊外也可使用它們。換句話說，區塊並不定義了範圍。JavaScript 中的"獨立"區塊將會產生與 C 和 Java 中不同的效果。舉例來說：

```js
var x = 1;
{
  var x = 2;
}
console.log(x); // 輸出 2
```

這裏輸出了 2 是因為區塊中變數 `var x` 陳述式擁有與區塊外的 `var x` 相同的有效範圍。在 C 或 Java，相同的程式碼將會輸出 1。

從 ECMAScript2015 版本起， 使用 `let` 定義的變數範圍將被限制於區塊內。

條件陳述式
-----

條件陳述式是一些在指定條件爲真下將被執行的指令。 JavaScript 支援兩種條件陳述式： `if...else` 和 `switch`。

### `if...else` 陳述式

`if 陳述式將在「邏輯判斷爲真」下執行接下來的一個陳述式`。選擇性的 `else` 陳述式將會在「邏輯判斷爲否」時被執行。 `if` 陳述式的用法看起來如下：
```

if (指定條件) {
  陳述式 1;
} else {
  陳述式 2;
}
```

指定條件可以是任何會回傳 true 或 false 的運算式。參見 Boolean 來進一步瞭解哪些運算式會回傳 `true` 及 `false`。假如指定條件爲 `true`，陳述式 1 會被執行；否則，陳述式 2 會被執行。陳述式 1 及陳述式 2 可以是任何陳述式，包含巢狀 `if` 陳述式。

你也可以藉由 `else if` 來使用複合的陳述式來測試多種不同的條件，如下：
```

if (指定條件1) {
  陳述式 1;
} else if (指定條件 2) {
  陳述式 2;
} else if (指定條件 n) {
  陳述式 n;
} else {
  最後陳述式;
}
```

在多個條件中，只有第一個條件爲 true 的陳述式會被執行。若要執行多個陳述式，可以將陳述式包在同一個程式區塊中（`{ ... }`）。 通常來說，使用區塊陳述式是很好的習慣，尤其在使用巢狀 if 的時候:
```

if (指定條件) {
  陳述式_1_執行_當_指定條件_爲_真;
  陳述式_2_執行_當_指定條件_爲_真;
} else {
  陳述式_3_執行_當_指定條件_爲_否;
  陳述式_4_執行_當_指定條件_爲_否;
}
```

建議不要在以賦值作爲條件運算式，因為"賦值"常常被和"等於"搞混。 例如， 不要寫出如下面的程式碼:

```js
if (x = y) {
  /\* 陳述式 \*/
}
```

如果你真的需要以賦值作爲條件運算式，一種常見的方式是額外在賦值式外面加上一組括號。例如：

```js
if ((x = y)) {
  /\* 陳述式 \*/
}
```

#### 爲"否"的值

下列的值會被看作 false（也稱為 Falsy (en-US) 值）

* `false`
* `undefined`
* `null`
* `0`
* `NaN`
* 空字串(`""`)

其他所有的值，包含所有物件，當被作為條件陳述式都會被視為 `true`。

不要把"布林真假值"和"布林物件的真假值"弄混了。 例如:

```js
var b = new Boolean(false);
if (b) // 這會是 True
if (b == true) // 這會是 false
```

#### 範例

在下面的範例中，函式 `checkData` 回傳 `true` 當 `Text` 物件的長度爲三；否則，顯示出 alert 並回傳 `false`。

```js
function checkData() {
  if (document.form1.threeChar.value.length == 3) {
    return true;
  } else {
    alert(
      "請輸入恰好三個字元. " +
        document.form1.threeChar.value +
        " is not valid.",
    );
    return false;
  }
}
```

### `switch` 陳述式

`switch` 陳述式允許程式依運算式的不同值來選擇不同標籤。 假如運算式和標籤匹配，程式會執行標籤對應的陳述式。範例如下:
```

switch (運算式) {
  case 標籤 1:
    陳述式 1
    [break;]
  case 標籤 2:
    陳述式 2
    [break;]
    ...
  default:
    陳述式
    [break;]
}
```

程序首先尋找一個標籤與運算式的值匹配的 `case` 子句，然後將控制權轉移給該子句，執行與其相關的陳述式。 如果沒有找到匹配的標籤，程序將查找 `default` 子句（選擇性），如果找到，則將控制權轉移到該子句，執行關聯的陳述式。 如果沒有找到 `default` 子句，程序繼續在 `switch` 結束後的陳述式執行。 按照慣例，默認子句是最後一個子句，但並不硬性規定。

與每個 `case` 子句相關聯的 `break` 陳述式（選擇性）確保程序在發現匹配的陳述式之後退出 `switch`，並在 `switch` 後的陳述式中繼續執行。 如果省略 `break`，程序將繼續在 `switch` 陳述式中的下一個陳述式執行。

#### 範例

在下面範例中，如果變數 `fruittype` 為「Bananas」，程序將與「Bananas」匹配並執行相關陳述式。 當遇到 `break` 時，程序離開 `switch` 並執行 `switch` 後的陳述式。 如果省略 `break`，也將執行 case 「Cherries」的陳述式。

```js
switch (fruittype) {
  case "Oranges":
    console.log("Oranges are $0.59 a pound.");
    break;
  case "Apples":
    console.log("Apples are $0.32 a pound.");
    break;
  case "Bananas":
    console.log("Bananas are $0.48 a pound.");
    break;
  case "Cherries":
    console.log("Cherries are $3.00 a pound.");
    break;
  case "Mangoes":
    console.log("Mangoes are $0.56 a pound.");
    break;
  case "Papayas":
    console.log("Mangoes and papayas are $2.79 a pound.");
    break;
  default:
    console.log("Sorry, we are out of " + fruittype + ".");
}
console.log("Is there anything else you'd like?");
```

例外處理陳述式
-------

你可以用以 `throw` 陳述式丟出例外，並以 `try...catch` 陳述式處理之。

* `throw` 陳述式
* `try...catch` 陳述式
### 例外的形態

任何物件（object）都可以在 JavaScript 中被拋出。 然而，並非所有拋出的物件都相同。 雖然將數字或字串作為錯誤物件使用是相當常見的，但使用為此目的專門創造的一種例外物件類型通常更有效:

* ECMAScript 例外
* `DOMException` (en-US) and `DOMError` (en-US)
### `throw` 陳述式

使用 `throw` 陳述式拋出例外。當拋出例外時，你要指定包含在要拋出物件中的值:
```

throw expression;
```

你可以拋出任何運算式，而不僅僅是特定類型的運算式。以下的程式碼會拋出一些不同類型的例外：

```js
throw "Error2"; // 字串形態
throw 42; // 數字形態
throw true; // True/False
throw {
  toString: function () {
    return "我是物件!";
  },
};
```

**備註：** 你可以在拋出例外時指定物件。然後，可以在 catch 區塊中引用對象的屬性。

```js
// 創建類型爲 UserException 的物件
function UserException(message) {
  this.message = message;
  this.name = "UserException";
}

// 讓例外轉換成整齊的字串當它被當作字串使用時
// （舉例來說:於 error console）
UserException.prototype.toString = function () {
  return this.name + ': "' + this.message + '"';
};

// 創建一個物件的實例並丟出它
throw new UserException("Value too high");
```

### `try...catch` 陳述式

`try...catch` 陳述式標記了一組要嘗試的陳述式，並在拋出例外時指定一個或多個響應。 如果例外被拋出，`try...catch` 陳述式捕獲它。

`try...catch` 陳述式包括一個 `try` 區塊，它包含一個或多個陳述式，零個或多個 `catch` 區塊，包含在 `try` 區塊中拋出例外時該做什麼的陳述式。 也就是說，你希望 `try` 區塊成功，如果它不成功，你希望控制權傳遞給 `catch` 區塊。 如果 `try` 區塊內的任何陳述式（或在 `try` 區塊內調用的函數中）拋出例外，則控制立即切換到 `catch` 區塊。 如果在 `try` 區塊中沒有拋出例外，則跳過 `catch` 區塊。 `finally` 區塊在 `try` 和 `catch` 區塊執行後執行，但在 `try...catch` 陳述式之後的陳述式之前執行。

以下的範例使用 `try...catch` 陳述式。該範例調用基於傳遞給函數的值並從陣列中檢索月份名稱的函數。如果值不對應於月份數（1-12），則會拋出一個例外，其值為 "InvalidMonthNo"，並且 `catch` 區塊中的陳述式將 `monthName` 變數設置為 `unknown`。

```js
function getMonthName(mo) {
  mo = mo - 1; // Adjust month number for array index (1 = Jan, 12 = Dec)
  var months = [
    "Jan",
    "Feb",
    "Mar",
    "Apr",
    "May",
    "Jun",
    "Jul",
    "Aug",
    "Sep",
    "Oct",
    "Nov",
    "Dec",
  ];
  if (months[mo]) {
    return months[mo];
  } else {
    throw "InvalidMonthNo"; //throw 關鍵字在這裏被使用
  }
}

try {
  // statements to try
  monthName = getMonthName(myMonth); // 函式可以丟出例外
} catch (e) {
  monthName = "unknown";
  logMyErrors(e); // 將例外傳至例外處理機制
}
```

#### `catch` 區塊

你可以使用 `catch` 區塊來處理 `try` 區塊可能丟出的例外。
```

catch (catchID) {
  陳述式
}
```

`catch` 區塊指定用來保存 `throw` 陳述式所丟出的值的標識符（前面語法中的 `catchID`） 你可以使用此標識符獲取有關被拋出的例外的信息。 JavaScript 在進入`catch` 區塊時創建此標識符; 標識符僅持續 `catch` 區塊的持續時間；在 `catch` 區塊完成執行後，標識符不再可用。

例如，下列的程式碼中丟出了一個例外，當例外發生後，控制權被轉交給 `catch` 區塊。

```js
try {
  throw "myException"; // 產生例外
} catch (e) {
  // 用於處理例外的陳述式
  logMyErrors(e); // 將例外物件傳給 error handler
}
```

#### `finally` 區塊

`finally` 區塊中包含在 `try` 和 `catch` 區塊執行之後但在 `try...catch` 陳述式之後的陳述式之前 執行的陳述式。 無論是否拋出例外，`finally` 區塊都會執行。 如果拋出例外，則即使沒有 `catch` 區塊處理例外，`finally` 區塊中的陳述式也會執行。

你可以使用 `finally` 區塊來使腳本在發生例外時正常地結束。例如，你可能需要釋放腳本中綁定的資源。 在以下示例中，打開一個文件，然後執行使用該文件的陳述式（伺服器端 JavaScript 允許你訪問文件）。 如果在打開文件時拋出例外，`finally` 區塊會在腳本結束之前關閉文件。

```js
openMyFile();
try {
  writeMyFile(theData); // 可能產生例外
} catch (e) {
  handleError(e); // 處理可能發生的例外
} finally {
  closeMyFile(); // 總是在 try 結束後關閉檔案
}
```

如果 `finally` 區塊有返回值，那麼該值將成為整個 `try-catch-finally` 過程的返回值，而捨棄 `try` 和 `catch` 區塊中的任何返回陳述式：

```js
function f() {
  try {
    console.log(0);
    throw "bogus";
  } catch (e) {
    console.log(1);
    return true; // 這個回傳會被擱置
    // 直到 finally 區塊結束
    console.log(2); // 不會到達這裏
  } finally {
    console.log(3);
    return false; // 覆寫先前的 "return"
    console.log(4); // 不會到達這裏
  }
  // "return false" 在這裏被執行
  console.log(5); // 不會到達這裏
}
f(); // console 0, 1, 3; 會回傳false
```

`finally` 區塊覆寫返回值也適用於在 `catch` 區塊中拋出或重新拋出的例外（即便在`catch` 中再次丟出例外，`catch` 所屬的 `finally` 區塊還是會被執行）：

```js
function f() {
  try {
    throw "bogus";
  } catch (e) {
    console.log('caught inner "bogus"');
    throw e; // 此處的 throw 陳述式將被擱置到
    // finally 區塊結束
  } finally {
    return false; // 覆寫先前的"throw"
  }
  // "return false" 在此被執行
}

try {
  f();
} catch (e) {
  // 這裏永遠不可能到達因為在 f 函式中 catch 的 throw
  // 被 finally 中的 return 覆寫了
  console.log('caught outer "bogus"');
}

// 輸出 -> caught inner "bogus"
```

#### 巢狀 try...catch 陳述式

你可以使用一個或多個的 `try...catch` 陳述式。 假如一個內層的`try...catch` 陳述式不具有 `catch` 區塊， 它將必須要有 `finally` 區塊與及封閉的 `try...catch` 陳述式來檢測是否有符合的例外。

### 使用 `Error` 物件

根據錯誤的類型，你可以使用 "name" 和 "message" 屬性來獲取更精確的資訊。"name" 提供了錯誤所屬的類別（class）（例如，"DOMException" 或 "Error"），而 "message" 通常提供藉由將錯誤物件轉換為字串所獲得的更簡潔的資訊。參見巢狀 try 區塊位於 `try...catch` 參考資料頁面。

假如你要丟出自定義的例外， 為了方便使用這些屬性（例如，如果你的 `catch` 區塊並不要區分你自己的例外和系統的），你可以使用 `Error` 構造子。舉例來說：

```js
function doSomethingErrorProne () {
  if (ourCodeMakesAMistake()) {
    throw (new Error('The message'));
  } else {
    doSomethingToGetAJavascriptError();
  }
}
....
try {
  doSomethingErrorProne();
}
catch (e) {
  console.log(e.name); // 紀錄 'Error'
  console.log(e.message); // 紀錄 'The message' 或者其他 JavaScript 例外的資訊)
}
```

Promises 容器
-----------

從 ECMAScript2015 起，JavaScript 支援 `Promise` 物件，允許你控制延遲和異步操作的流程。

`Promise` 有以下幾種狀態:

* *pending*：等待中，為初始之狀態，即不是 fulfilled 也不是 rejected。
* *fulfilled*：已實現，表示操作成功完成。
* *rejected*：已拒絕，表示操作失敗。
* *settled*：已完成，表示 Promise 狀態為已實現或已拒絕，但不是等待中。
### 使用 XHR 載入圖檔

這裏有個簡單的範例，使用了 `Promise` 物件與及 `XMLHttpRequest` 來載入 MDN GitHub promise-test repository 中的一張圖檔。你也可以觀看結果。 每一步都有註解來讓你慢慢理解 Promise 物件與及 XHR 架構。 下面的版本沒有註解，但藉由觀察 `Promise` 物件的變動你或許可以對 promise 物件有所了解:

```js
function imgLoad(url) {
  return new Promise(function (resolve, reject) {
    var request = new XMLHttpRequest();
    request.open("GET", url);
    request.responseType = "blob";
    request.onload = function () {
      if (request.status === 200) {
        resolve(request.response);
      } else {
        reject(
          Error(
            "Image didn't load successfully; error code:" + request.statusText,
          ),
        );
      }
    };
    request.onerror = function () {
      reject(Error("There was a network error."));
    };
    request.send();
  });
}
```

以獲得更多資訊，查看 `Promise` 參照頁面，以及使用 Promises 教學。

* « 前頁
* 次頁 »