<picture>: The Picture element - HTML：超文本標記語言

<picture>: The Picture element
==============================

**HTML `<picture>` 元素** 包含了零或零以上個 `<source>` (en-US) 元素以及一個 `<img>` (en-US) 元素，以為不同顯示器/裝置提供同張圖片的不同版本。

瀏覽器將會考慮每個 `<source>` 元素，並且在其中選出最適當的選項。如果沒有找到最適當的選項——或是瀏覽器不支援 `<picture>` 元素——則 `<img>` 屬性的 URL 會被選擇。被選擇的圖片將會在 `<img>` 元素存在的位置顯示。

嘗試一下
----

為了決定載入哪一個 URL，user agent (en-US) 會檢視每一個 `<source>` 的 `srcset` (en-US)、`media` (en-US) 以及 `type` (en-US) 屬性，以選出最適合當前版面以及顯示裝置支援度的圖片。

`<img>` 有兩個作用：

1. 它描述了該圖片的尺寸等屬性以及呈現（presentation）。
2. 在所有列出的 `<source>` 都不能提供可用圖片的情況下的 fallback。

`<picture>` 的常見使用案例：

* **圖像方向（art direction）：** 根據不同的 `media` 狀況裁切或調整圖片（例如在較小的螢幕上，載入原本有複雜細節圖片的較簡單版本圖片）
* \*\*提供替代的圖片格式：\*\*以應對某些特定格式不被支援的情況
* \*\*節省頻寬並加速頁面載入速度：\*\*透過針對觀看者的裝置載入最適當的圖片做到這點

如果是要為高 DPI （Retina）螢幕提供圖片的高解析度版本時，可改在使用 `<img>` 上使用 `srcset` (en-US) 屬性。這會讓瀏覽器在 data-saving 模式選擇低解析度的版本，這樣你就不用特地指定 `media` 條件。

| Content categories | Flow content, phrasing content, embedded content |
| --- | --- |
| Permitted content | Zero or more `<source>` (en-US) elements, followed by one `<img>` (en-US) element, optionally intermixed with script-supporting elements. |
| Tag omission | None, both the starting and ending tag are mandatory. |
| Permitted parents | Any element that allows embedded content. |
| Implicit ARIA role | No corresponding role |
| Permitted ARIA roles | No `role` permitted |
| DOM interface | `HTMLPictureElement` (en-US) |

屬性
--

此元素只包含 global attributes (en-US).

用法筆記
----

你可以使用 `object-position` (en-US) 屬性來在元素的 frame 內調整圖片位置，也可以用 `object-fit` (en-US) 屬性控制圖片在 frame 內如何調整大小。

**備註：** 在子元素 `<img>` 上使用這些屬性，而不是 `<picture>` 元素.

範例
--

以下例子示範如何根據 `<source>` (en-US) 元素不同的屬性設定選擇 `<picture>` 中的不同圖片。

### media 屬性

`media` 屬性指定特定的媒體類型（跟 media query 很像），讓 user agent 可對每個 `<source>` (en-US) 元素作出判斷。

如果 `<source>` (en-US) 的指定媒體類型被判斷為 `false` ，則瀏覽器會跳過它，並繼續判斷 `<picture>` 中的下個元素。

```html
<picture>
  <source srcset="mdn-logo-wide.png" media="(min-width: 600px)" />
  <img src="mdn-logo-narrow.png" alt="MDN" />
</picture>
```

### srcset 屬性

`srcset` (en-US) 屬性用來提供根據大小區分的可能圖片清單。

它是由逗號分隔的圖片描述句清單組成的。每一個圖片描述句是由該圖片的 URL 以及以下描述組成（擇一）：

* 寬度，結尾為 `w` (例如 `300w`)；
 *或是*
* 像素密度，結尾為 `x` (例如 `2x`)，以為高 DPI 螢幕提供高解析度圖片

```html
<picture>
  <source srcset="logo-768.png 768w, logo-768-1.5x.png 1.5x" />
  <source srcset="logo-480.png, logo-480-2x.png 2x" />
  <img src="logo-320.png" alt="logo" />
</picture>
```

### type 屬性

`type` 屬性為 `<source>` (en-US) 元素中 `srcset` 屬性的資源 URL 指定 MIME type (en-US) 。如果 user agent 不支援該 type 的話，此 `<source>` (en-US) 元素會被略過。

```html
<picture>
  <source srcset="logo.webp" type="image/webp" />
  <img src="logo.png" alt="logo" />
</picture>
```

規格
--

| Specification |
| --- |
| HTML Standard # the-picture-element |

瀏覽器支援度
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

相關資源
----

* `<img>` (en-US) 元素
* `<source>` (en-US) 元素
* 在圖片的 frame 中調整其大小與位置： `object-position` (en-US) and `object-fit` (en-US)
* 圖片檔案類型與格式指南 (en-US)


<br>（斷行元素） - HTML：超文本標記語言

<br>（斷行元素）
==========

**HTML `<br>` 元素**會產生文字的斷行（carriage-return、CR 或是確認鍵）。此元素主要用於斷行有所意義的時候，像是寫詩或寫住址。

嘗試一下
----

如上所示，`<br>` 元素會在需要斷行的時候出現。在 `<br>` 之後的文字會從文字區域的下一行出現。

**備註：** 不要用 `<br>` 建立段落間距：這種情況下要用 `<p>` (en-US) 把它們包起來，並使用 CSS `margin` (en-US) 屬性控制間距大小。

屬性
--

這個元件屬性含有全域屬性 (en-US)。

### 棄用屬性

`clear`

指示中斷後下一行的開始位置。

使用 CSS 樣式化
----------

`<br>` 元素有一個定義明確的用途：在文字區塊裡面建立斷行。因此它本身沒有尺寸或視覺輸出，基本上你做不了任何樣式化。

你可以給 `<br>` 元素設置 `margin` (en-US) 以增加文字之間的斷行大小，但這並不是正確的作法：你應該用 `line-height` (en-US) 屬性做這件事情。

示例
--

```html
Mozilla Foundation<br />
1981 Landings Drive<br />
Building K<br />
Mountain View, CA 94043-0801<br />
USA
```

以上 HTML 將顯示

無障礙議題
-----

使用 `<br>` 元素*分段*不只作法不佳，對於使用輔具的人來說，也會是有問題的。螢幕閱讀器會念出該元素，但 `<br>` 裡面的內容是念不出來的。對於使用螢幕閱讀器的人來說，這可能是困惑與沮喪的體驗。

請使用 `<p>` 元素搭配 CSS 屬性如 `margin` (en-US) 來控制間距大小。

技術摘要
----

| 內容類型 (en-US) | 流型內容 (en-US)、段落型內容 (en-US)。 |
| --- | --- |
| 允許內容 | 無，這是個空元素. |
| 標籤省略 | 絕對要有開啟標籤，也絕不能關閉標籤。在 XHTML 文件內，要把這個元素寫成 `<br />`. |
| 允許父元素 | 任何接受段落型內容 (en-US)的元素 |
| 允許的 ARIA roles | 所有 |
| DOM 介面 | `HTMLBRElement` (en-US) |

規範
--

| Specification |
| --- |
| HTML Standard # the-br-element |

瀏覽器相容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

參見
--

* `<address>` (en-US) 元素
* `<p>` (en-US) 元素
* `<wbr>` (en-US) 元素


<font> - HTML：超文本標記語言

<font>
======

**已棄用:** 不推薦使用此功能。雖可能有一些瀏覽器仍然支援它，但也許已自相關的網頁標準中移除、正準備移除、或僅為了維持相容性而保留。避免使用此功能，盡可能更新現有程式；請參考頁面底部的相容性表格來下決定。請注意：本功能可能隨時停止運作。

概要
--

*HTML Font 元素*（`<font>`）定義了該內容的字體大小、顏色與表現。

**備註：** 不要使用這個元素！儘管它在 HTML 3.2 規範化，但在 HTML 4.01，因為該元件只有裝飾用途的理由而遭棄用，接著在 HTML5 廢棄。從 HTML 4 開始，HTML 不能在 `<style>` (en-US) 元素，或各元素 **style** 屬性以外，表現任何樣式資訊。今後的網頁開發，樣式化要用 CSS。`<font>` 元素的行為，能透過 CSS 屬性實現，也更好控制。

屬性
--

如同其他 HTML 元素，這個元素支援全域屬性 (en-US)。

`color`

這個屬性可以藉由顏色名字或是十六進位的 #RRGGBB 格式，來配置文字的顏色。

`face`

這個屬性列出了一個或多個逗號分隔的字體名稱。 The document text in the default style is rendered in the first font face that the client's browser supports. 如果在用戶端的系統並沒有安裝這個字體，瀏覽器會在該系統使用預設的 proportional 或是 fixed-width 字體。

`size`

這個屬性藉由數字或相對值指定文字大小。數字是由最小的 `1` 到最大的 `7` 之間，且預設為 `3` 的數值表示。也可以用諸如 `+2` 或 `-3` 的相對值指定, which set it relative to the value of the `size` (en-US) attribute of the `<basefont>` (en-US) element, or relative to 3, the default value, if none does exist.

DOM 介面
------

這個元素 implements `HTMLFontElement` (en-US) 介面。

瀏覽器相容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


<table>（表格元素） - HTML：超文本標記語言

<table>（表格元素）
=============

**HTML `<table>` 元件**代表表格數據 ── 換句話說，就是透過二維資料表來呈現資訊。

嘗試一下
----

|  |  |
| --- | --- |
| 內容類型 (en-US) | 流內容 (en-US) |
| 允許內容 | 按照以下順序：
 1. 一個可選的`<caption>` (en-US)元素，
2. 零個或更多的`<colgroup>` (en-US)元素，
3. 一個可選的`<thead>` (en-US)元素，
4. 一個在以下元素之前或之後的可選 `<tfoot>` (en-US)
 元素：
    * 零個或更多的`<tbody>` (en-US)元素，
    * 或者，一個或更多的`<tr>` (en-US)元素

 |
| 標籤省略 | None, both the starting and ending tag are mandatory. |
| 允許父元素 | Any element that accepts flow content |
| 允許 ARIA 規則 | Any |
| DOM 介面 | `HTMLTableElement` (en-US) |

屬性
--

這個元件包含了 全域屬性（global attributes） (en-US)。

### 棄用屬性

`align`
已棄用

這個枚舉屬性會指示表格中的文字要如何對齊。可用值如下：left：意思是表格應該顯示在文件的左方。

* center：意思是表格應該顯示在文件的中間。
* right：意思是表格應該顯示在文件的右方。在 CSS 要得出類似效果，應該設定 `margin-left` (en-US) 與 `margin-right` (en-US)；如果要置中，則要把 `margin` (en-US) 屬性設定為 `0 auto`。

`bgcolor`
已棄用

定義表格的背景與內容顏色。它使用六位十六進制 RGB code (en-US)，前缀需要加上 '`#`' 。也可以用預先定義的顏色字串 (en-US)可用。在 CSS 要得出類似效果，應該使用 `background-color` 屬性。

`border`
已棄用

這個屬性以像素為單位，定義了圍繞於表格框架的大小。如果設為 0，代表 `frame` 屬性為空。在 CSS 要得出類似效果，應該使用 `border` (en-US) 屬性。

`cellpadding`
已棄用

這個屬性定義了元件與邊界線之間的空白，以及要不要顯示。如果輸入像素，像素長度的空白會套用到四個邊；如果輸入百分比，內容將居中、整體的垂直空間（上與下）會使用這個百分比表示。橫向空間（左與右）也適用這個規則。在 CSS 要得出類似效果，應該對 `<table>` 屬性使用 `border-collapse` (en-US)；並對 `<td>` (en-US) 使用 `padding` (en-US)。

`cellspacing`
已棄用

This attribute defines the size of the space between two cells in a percentage value or pixels. The attribute is applied both horizontally and vertically, to the space between the top of the table and the cells of the first row, the left of the table and the first column, the right of the table and the last column and the bottom of the table and the last row.To achieve a similar effect, apply the `border-spacing` (en-US) property to the `<table>` element. `border-spacing` does not have any effect if `border-collapse` (en-US) is set to collapse.

`frame`
已棄用

這個枚舉屬性定義圍繞在表格邊框的哪一邊要顯示。在 CSS 要得出類似效果，應該使用 `border-style` (en-US) 與 `border-width` (en-US) 屬性。

`rules`
已棄用

這個枚舉屬性定義諸如線條之類的規則，要如何出現在表格。它擁有以下數值：`none`，代表沒有任何規則上的指示。這是預設值。

* `groups`，只標示行群組和列群組（行群組由 `<thead>` (en-US)、`<tbody>` (en-US)、和 `<tfoot>` (en-US) 定義；列群組由 `<col>` (en-US) 與 `<colgroup>` (en-US) 定義）；
* `rows`，會創立一組標示行的規則；
* `columns`，會創立一組標示列的規則；
* `all`，會創立一組同時標示行與列的規則。在 CSS 要得出類似效果，應該針對 `<thead>` (en-US)、`<tbody>` (en-US)、`<tfoot>` (en-US)、`<col>` (en-US)、`<colgroup>` (en-US) 元素使用 `border` (en-US) 屬性。

`summary`
已棄用

這個屬性定義了總結表格的替代文字。請改用 `<caption>` (en-US) 元素。

`width`
已棄用

這個屬性定義了表格的寬度。請改用 CSS `width` 屬性。

範例
--

### 簡單的表格

```html
<table>
  <tr>
    <td>John</td>
    <td>Doe</td>
  </tr>
  <tr>
    <td>Jane</td>
    <td>Doe</td>
  </tr>
</table>
```

### 更多範例

```html
<p>Simple table with header</p>
<table>
  <tr>
    <th>First name</th>
    <th>Last name</th>
  </tr>
  <tr>
    <td>John</td>
    <td>Doe</td>
  </tr>
  <tr>
    <td>Jane</td>
    <td>Doe</td>
  </tr>
</table>

<p>Table with thead, tfoot, and tbody</p>
<table>
  <thead>
    <tr>
      <th>Header content 1</th>
      <th>Header content 2</th>
    </tr>
  </thead>
  <tfoot>
    <tr>
      <td>Footer content 1</td>
      <td>Footer content 2</td>
    </tr>
  </tfoot>
  <tbody>
    <tr>
      <td>Body content 1</td>
      <td>Body content 2</td>
    </tr>
  </tbody>
</table>

<p>Table with colgroup</p>
<table>
  <colgroup span="4"></colgroup>
  <tr>
    <th>Countries</th>
    <th>Capitals</th>
    <th>Population</th>
    <th>Language</th>
  </tr>
  <tr>
    <td>USA</td>
    <td>Washington D.C.</td>
    <td>309 million</td>
    <td>English</td>
  </tr>
  <tr>
    <td>Sweden</td>
    <td>Stockholm</td>
    <td>9 million</td>
    <td>Swedish</td>
  </tr>
</table>

<p>Table with colgroup and col</p>
<table>
  <colgroup>
    <col style="background-color: #0f0" />
    <col span="2" />
  </colgroup>
  <tr>
    <th>Lime</th>
    <th>Lemon</th>
    <th>Orange</th>
  </tr>
  <tr>
    <td>Green</td>
    <td>Yellow</td>
    <td>Orange</td>
  </tr>
</table>

<p>Simple table with caption</p>
<table>
  <caption>
    Awesome caption
  </caption>
  <tr>
    <td>Awesome data</td>
  </tr>
</table>
```

```
table {
  border-collapse: collapse;
  border-spacing: 0px;
}
table,
th,
td {
  padding: 5px;
  border: 1px solid black;
}
```

無障礙議題
-----

### Caption

提供 `<caption>` (en-US) 元素，以便清晰而簡潔地描述表格主旨。他能讓用戶決定自己是否該閱讀表格內容，還是要略過就好。

如此也能幫助螢幕閱讀器之類的輔具使用者、視力條件差、還有認知障礙的人。

* MDN Adding a caption to your table with <caption> (en-US)
* Caption & Summary • Tables • W3C WAI Web Accessibility Tutorials
### Scope 行列

雖然在 HTML5 裡面 `scope` (en-US) 屬性已經過時，但很多螢幕閱讀器會利用這屬性，複製不使用屏幕閱讀器的人的視覺關聯，以便推斷可能的視覺位置。

#### 示例

```html
<table>
  <caption>
    Color names and values
  </caption>
  <tbody>
    <tr>
      <th scope="col">Name</th>
      <th scope="col">HEX</th>
      <th scope="col">HSLa</th>
      <th scope="col">RGBa</th>
    </tr>
    <tr>
      <th scope="row">Teal</th>
      <td><code>#51F6F6</code></td>
      <td><code>hsla(180, 90%, 64%, 1)</code></td>
      <td><code>rgba(81, 246, 246, 1)</code></td>
    </tr>
    <tr>
      <th scope="row">Goldenrod</th>
      <td><code>#F6BC57</code></td>
      <td><code>hsla(38, 90%, 65%, 1)</code></td>
      <td><code>rgba(246, 188, 87, 1)</code></td>
    </tr>
  </tbody>
</table>
```

在 `<th>` (en-US) 元素提供 `scope="col"` 的宣告，有助於描述該單位屬於第一列。在 `<td>` (en-US) 元素提供 `scope="row"` 則有助於描述該單位屬於第一行。

* MDN Tables for visually impaired users (en-US)
* Tables with two headers • Tables • W3C WAI Web Accessibility Tutorials
* Tables with irregular headers • Tables • W3C WAI Web Accessibility Tutorials
* H63: Using the scope attribute to associate header cells and data cells in data tables | W3C Techniques for WCAG 2.0
### 複雜的表格

針對單格複雜到無法歸類於直向或橫向的表格，諸如螢幕閱讀器之類的輔助技術，可能就無法解析。在這種情況下，通常就需要 `colspan` (en-US) 與 `rowspan` (en-US) 屬性。

理想情況下，可以考慮使用其他方式來呈現表格的內容，例如把表格切分到不必依賴 `colspan` 和 `rowspan` 屬性。除了幫助使用輔助技術的人了解表格的內容之外，這樣也會使認知障礙者受益，因為他們可能難以理解表格佈局描述的關聯。

`如果表格無法切分，請結合 [`id`](/zh-TW/docs/Web/HTML/Global_attributes#id) 與 [`headers`](/zh-TW/docs/Web/HTML/Element/td#headers) 使用，以便程序化地關聯各表格單位與標題。`

* `MDN Tables for visually impaired users` (en-US)
* `Tables with multi-level headers • Tables • W3C WAI Web Accessibility Tutorials`
* `H43: Using id and headers attributes to associate data cells with header cells in data tables | Techniques for W3C WCAG 2.0`
規範
--

| Specification |
| --- |
| HTML Standard # the-table-element |

瀏覽器相容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

參見
--

* CSS properties that may be especially useful to style the `<table>` element:
    + `width` to control the width of the table;
    + `border` (en-US), `border-style` (en-US), `border-color`, `border-width` (en-US), `border-collapse` (en-US), `border-spacing` (en-US) to control the aspect of cell borders, rules and frame;
    + `margin` (en-US) and `padding` (en-US) to style the individual cell content;
    + `text-align` (en-US) and `vertical-align` (en-US) to define the alignment of text and cell content.


<frameset> - HTML：超文本標記語言

<frameset>
==========

**已棄用:** 不推薦使用此功能。雖可能有一些瀏覽器仍然支援它，但也許已自相關的網頁標準中移除、正準備移除、或僅為了維持相容性而保留。避免使用此功能，盡可能更新現有程式；請參考頁面底部的相容性表格來下決定。請注意：本功能可能隨時停止運作。

概要
--

`<frameset>` 是用以包含 `<frame>` (en-US) 元素的元素。

**備註：** 因為使用框架的推薦是 `<iframe>` (en-US)，這個元素一般不會在現在的網站出現。

屬性
--

如同其他 HTML 元素，這個元素支持全域屬性。

`cols`

這個屬性指定了橫向框架的數量和尺寸。

`rows`

這個屬性指定了直向框架的數量和尺寸。

範例
--

```html
<frameset cols="50%,50%">
  <frame src="https://developer.mozilla.org/en/HTML/Element/frameset" />
  <frame src="https://developer.mozilla.org/en/HTML/Element/frame" />
</frameset>
```

參見
--

* `<frame>` (en-US)
* `<iframe>` (en-US)


<hr> - HTML：超文本標記語言

<hr>
====

**HTML** 的\*\* `<hr>` 元素\*\*代表在段落層級的焦點轉換（如故事中的場景轉換或某個小節裡的主題移轉）。在之前的 HTML 版本，它代表著一條水平標線。在視覺瀏覽器裡，它現在可能還是以水平標線的型式呈現；但它已經被重新定義為一個語義上的用詞，而不是呈現上的。

| 內容類型 (en-US) | 流內容 (en-US). |
| --- | --- |
| Permitted content | 否。這是個 empty element. |
| 標籤省略 | 一定要有起始標籤、同時絕不能有結束標籤 |
| Permitted parent elements | 任何允許流內容 (en-US)的元素。 |
| DOM interface | `HTMLHRElement` (en-US) |

屬性
--

這個元素支持全域屬性 (en-US)。

`align`
已棄用

設罝頁面上標線的對齊方式。如果沒有指定，預設值是：`left。`

`color`
非標準

用色彩名或 16 進位值設罝標線的顏色。

`noshade`
已棄用

設置這個標線沒有陰影。

`size`
已棄用

設置標線的高度，單位是 px。.

`width`
已棄用

設置標線的長度，單位是 px；或者也可以用頁面寛度的百分比 (%)表示。

範例
--

```html
<p>
  This is the first paragraph of text. This is the first paragraph of text. This
  is the first paragraph of text. This is the first paragraph of text.
</p>

<hr />

<p>
  This is second paragraph of text. This is second paragraph of text. This is
  second paragraph of text. This is second paragraph of text.
</p>
```

上面的 HTML 會輸出：

This is the first paragraph of text. This is the first paragraph of text. This is the first paragraph of text. This is the first paragraph of text.

---

This is second paragraph of text. This is second paragraph of text. This is second paragraph of text. This is second paragraph of text.

規範
--

| Specification |
| --- |
| HTML Standard # the-hr-element |

瀏覽器相容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

參照 （see also)
-------------

* `<p>` (en-US)


<time> - HTML：超文本標記語言

<time>
======

**HTML `<time>` element** 用以表示時間，可以是 24 小時制時間或以公曆紀年的日期（日期可以添加時間和時區的資訊）

此 element 旨在以機器可讀的格式顯示時間。這能幫助使用者代理調度使用者的日期。

**備註：** 此 element 不適合用在「不確定正確時間」和「時間為西元前（日期的計算會出現錯誤）」的情況。

| 內容分類 | Flow content, phrasing content (en-US), palpable content. |
| --- | --- |
| 允許之內容 | Phrasing content (en-US). |
| 省略標籤 | 不可。起始標籤和結束標籤是強制必要的 |
| 可接受的副 elements | 任何接受 phrasing content 的 element。 |
| DOM interface | `HTMLTimeElement` (en-US) |

Attributes
----------

和其他 HTML elements 一樣，接受所有 global attributes (en-US)。

`datetime`

此 attribute 用以表示日期或時間，其格式必須是有效的日期/時間格式。若其值不正確，則 element 就不會有關連的 time stamp.

示例
--

### Simple example

#### HTML

```html
<p>The concert starts at <time>20:00</time>.</p>
```

#### Output

### `datetime` example

#### HTML

```html
<p>
  The concert took place on <time datetime="2001-05-15T19:00">May 15</time>.
</p>
```

#### Output

規格
--

| Specification |
| --- |
| HTML Standard # the-time-element |

瀏覽器支援度
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

延伸閱讀
----

* The `<data>` (en-US) element, allowing to signal other kind of values.


<template> - HTML：超文本標記語言

<template>
==========

**HTML `<template>` 元素**是用作保存用戶端內容的機制。該內容在頁面載入時不受渲染，但可以在運行時使用 JavaScript 實例化。

你可以把 template 想成文件裡面，被儲存以待稍後使用的內容片段。在頁面載入時，解析器雖然會處理 `<template>` 元件的內容，但元素本身並不會被渲染。

| 內容類型 | 元內容、流內容、phrasing content、支援腳本的元素 |
| --- | --- |
| 允許內容 | 沒有限制 |
| 標籤省略 | None, both the starting and ending tag are mandatory. |
| 允許的父元素 | `<body>` (en-US), `<frameset>`, `<head>`, `<dl>` (en-US) and `<colgroup>` (en-US) without a `span` attribute |
| 允許的 ARIA roles | 無 |
| DOM 介面 | `HTMLTemplateElement` (en-US) |

屬性
--

本元素只允許全域屬性 (en-US)。

示例
--

我們先從 HTML 部分開始。

```html
<table id="producttable">
  <thead>
    <tr>
      <td>UPC_Code</td>
      <td>Product_Name</td>
    </tr>
  </thead>
  <tbody>
    <!-- 在這裡可以選擇性地包括既有資料 -->
  </tbody>
</table>

<template id="productrow">
  <tr>
    <td class="record"></td>
    <td></td>
  </tr>
</template>
```

首先，我們有個稍後將透過 JavaScript 插入的表格。接著，我們把重點轉移到描述 HTML 內容模板結構的 template：它意味著一個表格的行。

現在表格已經建立、也定義了模板，所以我們將以 template 為基礎，用 JavaScript 把每個產生出來的行加到表格內。

```js
// 透過檢查 HTML template 元素屬性的存在與否，以測試瀏覽器是否支援它
if ("content" in document.createElement("template")) {
  // 使用現有 HTML tbody、行以及模板，來實例化表格
  var t = document.querySelector("#productrow"),
    td = t.content.querySelectorAll("td");
  td[0].textContent = "1235646565";
  td[1].textContent = "Stuff";

  // 複製新的行並將其插至表格
  var tb = document.querySelector("tbody");
  var clone = document.importNode(t.content, true);
  tb.appendChild(clone);

  // 複製新的行
  td[0].textContent = "0384928528";
  td[1].textContent = "Acme Kidney Beans";

  // 複製新的行並將其插至表格
  var clone2 = document.importNode(t.content, true);
  tb.appendChild(clone2);
} else {
  // 因為 HTML template 不被支援，所以要用其他方法在表格增加新行
}
```

結果會變成原生的 HTML 表格，它透過 JavaScript 產生了兩個新行：
```

table {
  background: #000;
}
table td {
  background: #fff;
}
```

規範
--

| Specification |
| --- |
| HTML Standard # the-template-element |

瀏覽器相容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

參閱
--

* Web component：`<slot>` (en-US)（還有過往的`<shadow>`）


<noscript> - HTML：超文本標記語言

<noscript>
==========

HTML **`<noscript>`** 元素定義了一個 HTML 區塊，如果網頁上的腳本類型不支援或瀏覽器當前已關閉腳本，則將插入該區塊。

|  |  |
| --- | --- |
| 內容類型 | 後設資料內容,
 流內容,
 段落型內容。
  |
| 允許內容 |
 當腳本被禁用時，並且該元素是
 `<head>` 元素的子元素時：可以包括零個或多個
 `<link>` (en-US) 元素、零個或多個
 `<style>` (en-US) 元素，以及零個或多個
 `<meta>` (en-US) 元素，它們的順序可以是任意的。當腳本被禁用時，並且該元素不是
 `<head>` 元素的後代時：可以包括任何
 透明內容，但不得包含 `<noscript>` 元素在其後代中。否則：可以包括流內容或段落型內
  |
| 標籤省略 | None, both the starting and ending tag are mandatory. |
| 允許的父元素 | 如果不存在祖元素 `<noscript>`，或者在 `<head>` 元素中（但僅適用於 HTML 文件），同樣如果不存在 `<noscript>` 祖元素，則接受任何段落型內容的元素。 |
| 隱含的 ARIA 角色 | 沒有對應的角色 |
| 允許的 ARIA 角色 | 沒有允許的 `role` |
| DOM 介面 | `HTMLElement` (en-US) |

屬性
--

這個元素屬性含有全域屬性 (en-US)。

範例
--

```html
<noscript>
  <!-- anchor linking to external file -->
  <a href="https://www.mozilla.org/">External Link</a>
</noscript>
<p>Rocks!</p>
```

### 腳本成功執行的結果

Rocks!

### 腳本執行失敗的成果

External Link

Rocks!

規範
--

| Specification |
| --- |
| HTML Standard # the-noscript-element |

瀏覽器相容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


<title> - HTML：超文本標記語言

<title>
=======

HTML **`<title>`** 元素定義了顯示在瀏覽器標題欄或頁面標籤上的文件標題。它僅包含文本，元素內的標籤會被忽略。

```html
<title>Grandma's Heavy Metal Festival Journal</title>
```

|  |  |
| --- | --- |
| 內容類型 | 後設資料內容。 |
| 允許內容 | 不包含元素間whitespace (en-US)的文字。 |
| 標籤省略 | 開起標籤和關閉標籤都是必需的。請注意，如果省略 `</title>` 標籤，瀏覽器將忽略頁面的其餘內容。 |
| 允許的父元素 | 不包含其他 `<title>` 元素的 `<head>` 元素。 |
| 隱含的 ARIA 角色 | 沒有對應的角色 |
| 允許的 ARIA 角色 | 沒有允許的 `role` |
| DOM 介面 | `HTMLTitleElement` (en-US) |

屬性
--

這個元件屬性含有全域屬性 (en-US)。

使用筆記
----

`<title>` 元素始終用於網頁的 `<head>` 區塊中。

### 網頁標題與 SEO

網頁標題的內容對搜索引擎優化（SEO）可能會有重要的影響。一般來說，長而具描述性的標題比簡短或通用的標題效果更好。標題的內容是搜索引擎算法用來決定在搜索結果中列出網頁的順序的元素之一。此外，標題是吸引讀者注意力的第一個「鉤子」，用來吸引他們瀏覽搜索結果頁面。

以下是一些撰寫好的標題的指南和建議：

* 避免使用單個詞或兩個詞的標題。使用一個具有描述性的短語，或者在詞彙表或參考頁面中使用詞語和定義的配對。
* 搜索引擎通常會顯示頁面標題的前55-60個字符。超出這個範圍的文本可能會被截斷，因此盡量不要使用超過這個長度的標題。如果必須使用較長的標題，請確保重要的部分位於前面，並且不要讓關鍵內容出現在可能被截斷的部分。
* 不要使用「關鍵詞堆疊」。如果標題只是一個詞語列表，算法通常會導致網頁在搜索結果中的排名下降。
* 盡量確保標題在網站內是獨一無二的。重複或幾乎重複的標題可能導致不準確的搜索結果。
範例
--

```html
<title>Awesome interesting stuff</title>
```

這個範例建立了一個網頁，其標題（顯示在視窗頂部或視窗標籤中）為「Awesome interesting stuff」。

無障礙議題
-----

提供準確且簡潔的標題來描述網頁的用途非常重要。

對於輔助技術使用者來說，一種常見的導航技巧是閱讀網頁標題，然後推測網頁包含的內容。這是因為進入網頁以確定其內容可能是一個耗時且可能令人困惑的過程。網站的每個頁面標題都應該是獨一無二的，最好首先顯示頁面的主要目的，然後是網站的名稱。遵循這種模式將有助於確保螢幕閱讀器首先宣佈網頁的主要目的。這比讓使用者在訪問同一個網站的每個頁面時先聽到網站名稱然後才是獨特的頁面標題，提供了更好的使用體驗。

### 範例

```html
<title>Menu - Blue House Chinese Food - FoodYum: Online takeout today!</title>
```

如果表單提交包含錯誤並且提交導致當前頁面重新呈現，則可以使用標題來幫助用戶了解其提交中的任何錯誤。例如，更新頁面`title`的值以反映重要的頁面狀態變化（像是表單驗證問題）。

```html
<title>
  2 errors - Your order - Sea Food Store - Food: Online takeout today!
</title>
```

**備註：** 目前，動態更新頁面標題不會被螢幕閱讀器自動宣佈。如果要更新頁面標題以反映頁面狀態的重大變化，則可能需要使用 ARIA Live Region (en-US)。

* MDN Understanding WCAG, Guideline 2.4 explanations (en-US)
* Understanding Success Criterion 2.4.2 | W3C Understanding WCAG 2.1
規範
--

| Specification |
| --- |
| HTML Standard # the-title-element |

瀏覽器相容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

參見
--

* SVG `<title>` (en-US) 元素


<canvas> - HTML：超文本標記語言

<canvas>
========

這個 **HTML `<canvas>` 元素** 可以被使用於對腳本程式(JaveScript) 繪圖 .舉例來說, 它能用來畫圖,組合照片,甚至作動畫. 你也許(應該) 在<canvas>區段內提供替代方案的內容 .這內容將能被一些不支援<canvas>以及無法使用 Javescript 功能的瀏覽器所渲染 .

更多文章關於 canvas, 請參考 canvas 主題

| Content 分類 (en-US) | 流程式 content (en-US), 段落式 content (en-US), 強調式 content (en-US), 可見式 content. |
| --- | --- |
| 被允許通過的內容 | 除了子元素不是 的元素、 <button>的元素、<input>的元素屬性是`checkbox`, `radio`, 或 `button` 之外且是透明內容模型但不是互動式 content (en-US) |
| Tag 省略 | 不能省略標籤<canvas> |
| 被允許通過的父親元素 | 任何強調式 content (en-US)的元素皆可通過 |
| DOM 介面 | HTMLCanvasElement |

屬性
--

這個元素的屬性包含全域屬性

`height`

在 CSS 中以 pixels 表示 座標的空間**高度**（預設是 150）

`moz-opaque`
非標準

讓 canvas 知道這個因素是否為半透明的。如果 canvas 知道不是半透明，則提高繪畫的效能

`width`

在 CSS 中以 pixels 表示 座標的空間**寬度**（預設是 300）

描述
--

### 需要 `</canvas>` 結尾

與不同之處,`<canvas>`需要`</canvas>` 結尾

### canvas 的大小設置

canvas 的大小設置可透過樣式表（stylesheet）被改變。可經由樣式修改圖像的縮放大小。

若呈現的效果圖似乎是扭曲變形的，可試著先修改`<canvas>`標籤中的`width`和`height`的屬性，而**不建議**使用 css 去控制。

範例
--

將下面這條範例加到你的 HTML 文件。如果你的瀏覽器並不支援 canvas 或不能讀取 canvas 時，則會回報 canvas 中的訊息。若想知道更多有用的回應訊息或替代 DOM 的註文，可以參考更多相似的註文。

```html
<canvas id="canvas" width="300" height="300">
  抱歉，你的瀏覽器並不支援 canvas 元素</canvas
>
```

如果你的 canvas 沒有使用透明度，可在<canvas>標籤設立`moz-opaque`屬性，提升它的繪圖效果。`moz-opaque` 尚未標準化，所以只適用在 Mozilla 的 效能引擎。

```html
<canvas id="mycanvas" moz-opaque></canvas>
```

規範
--

| Specification |
| --- |
| HTML Standard # the-canvas-element |

瀏覽器相容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

延伸閱讀
----

* MDN canvas portal
* Canvas tutorial
* Canvas cheat sheet
* Canvas-related demos
* Canvas introduction by Apple


<nav> - HTML：超文本標記語言

<nav>
=====

**HTML <nav> 元素**代表一個網頁中提供導航欄連結的區域，不管是當前網頁或者其他網頁，還是這個連結最終抵達的只是當前網頁的某個區塊，或者會抵達其他網頁，常見的導航欄範例有菜單、內容的表單，以及頁面本身。

| Content categories (en-US) | 流內容 (en-US)、sectioning content (en-US)、palpable content. |
| --- | --- |
| 允許內容 | 流內容 (en-US)。 |
| 省略標籤 | None, both the starting and ending tag are mandatory. |
| Permitted parents | Any element that accepts flow content (en-US). |
| Permitted ARIA roles | None |
| DOM interface | `HTMLElement` (en-US) |

屬性
--

此元素只有全域屬性 (en-US)。

使用筆記
----

* 不是所有的文件連結都需要在 `<nav>` 元素裡面，它僅適用於主要的導航鏈接塊。一般來說 `<footer>` (en-US) 元素常常有一個不需要在 `<nav>` 元素裡面的的鏈接列表。
* 文件可以有很多個 `<nav>` 元素。例如說，一個用做網站導覽、另一個用做頁內導覽。
* 面向身障用戶的螢幕閱讀器的用戶代理（User agent），可以決定要不要在初始渲染時省略這個元素的內容。
範例
--

在這個範例，`<nav>` 區塊用於包含連結的無序清單 (`<ul>` (en-US))。透過合適的 CSS，它能夠顯示為 sidebar、導覽列、以及下拉式選單。

```html
<nav class="menu">
  <ul>
    <li><a href="#">Home</a></li>
    <li><a href="#">About</a></li>
    <li><a href="#">Contact</a></li>
  </ul>
</nav>
```

規範
--

| Specification |
| --- |
| HTML Standard # the-nav-element |

瀏覽器相容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

參見
--

* Other section-related elements: `<body>` (en-US), `<article>` (en-US), `<section>` (en-US), `<aside>` (en-US), `<h1>` (en-US), `<h2>` (en-US), `<h3>` (en-US), `<h4>` (en-US), `<h5>` (en-US), `<h6>` (en-US), `<hgroup>` (en-US), `<header>` (en-US), `<footer>` (en-US), `<address>` (en-US);
* Sections and outlines of an HTML5 document.


<div> - HTML：超文本標記語言

<div>
=====

**HTML `<div>` 元素**（或是 *HTML 文件區塊元素*）是本質上不特別代表任何東西的通用内容流容器。它可以成為樣式化用途（使用 **class** 或 **id** 屬性）、或是共享如 **lang** 同一個屬性的元素集合。它應該在沒有其他適合的語義元素（例如`<article>` (en-US) 或 `<nav>` ）可用時才用。

| 內容類型 | 流內容、捫及內容。 |
| --- | --- |
| 允許內容 | 流內容 |
| Tag 省略 | None, both the starting and ending tag are mandatory. |
| 允許父元素 | 任何允許內容流的元素。 |
| DOM 介面 | `HTMLDivElement` (en-US) |

屬性
--

這個元素包含全域屬性 (en-US)。

**align** 元素已過時，不要使用它。

範例
--

```html
<div>
  <p>這裡可以是任何內容，例如 <p>、<table>。你說了算！</p>
</div>
```

### 結果

這裡可以是任何內容，例如 <p>、<table>。你說了算！

規範
--

| Specification |
| --- |
| HTML Standard # the-div-element |

瀏覽器相容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

詳閱
--

* 語義化的切片元素：`<section>` (en-US), `<article>` (en-US), `<nav>`, `<header>` (en-US), `<footer>` (en-US)
* 針對段落式內容樣式化的 `<span>` (en-US) 元素


<blockquote> - HTML：超文本標記語言

<blockquote>
============

摘要
--

**HTML `<blockquote>` 元素**（*HTML 區塊引言元素*）定義一段文字屬於引用。通常，這元素會透過縮排來呈現（要知道如何改變，請參考備註）。引言的 URL 來源可透過 **cite** 屬性賦予，而來源的文本形式可以使用 `<cite>` (en-US) 元素。

| 內容類別 (en-US) | 流內容 (en-US)、sectioning root、捫及內容。 |
| --- | --- |
| 允許內容 | 內容流 (en-US). |
| Tag 省略 | None, both the starting and ending tag are mandatory. |
| 允許父元素 | 任何允許內容流 (en-US)的元素 |
| DOM 介面 | `HTMLQuoteElement` (en-US) |

屬性
--

這個屬性包含全局屬性 (en-US)。

`cite`

一個指向被引用的原始文件或訊息的 URL 。這個屬性預期要指引到解釋內容的資訊，或是引言的援引。

範例
--

```html
<blockquote cite="http://developer.mozilla.org">
  <p>這是取自於 Mozilla Developer Center 的引言。</p>
</blockquote>
```

以上的 HTML 原始碼會輸出：

規範
--

| Specification |
| --- |
| HTML Standard # the-blockquote-element |

瀏覽器相容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

備註
--

要改變`<blockquote>`的縮進，請使用 CSS 的 `margin` (en-US) 屬性。

針對短篇引文請使用 `<q>` 元素。

延伸閱讀
----

* `<q>` 元素：用以表示單行的引用內容。
* `<cite>` (en-US) 元素：用以表示引用來源。


<q> - HTML：超文本標記語言

<q>
===

*HTML 引文元素* (**<q>**) 指示一段元素內的文字，屬於行內引文。這個元素適用於不須換行的短引文，針對長引文，請使用 `<blockquote>` 元素。

| 內容類別 (en-US) | 流內容 (en-US)、段落式內容 (en-US)、捫及內容。 |
| --- | --- |
| 允許內容 | 段落式內容 (en-US) |
| 標籤省略 | None, both the starting and ending tag are mandatory. |
| 允許父元素 | 任何允許段落式內容 (en-US)的元素 |
| DOM 介面 | `HTMLQuoteElement` (en-US) |

**備註：** 多數現代標準瀏覽器，如 Mozilla Firefox, Opera, Safari 會在 `<q>` 元素周圍添加引號；但有些瀏覽器（如 Internet Explorer）可能不會針對引用添加任何樣式，不過這些都可以透過樣式規則套用。

屬性
--

這個元素包含 全域屬性 (en-US)。

`cite`

這個屬性值為可以指定引用文件、或信息來源的 URL。這個屬性預期導向信息說明的上下文、或是引文參照。

範例
--

### HTML 內容

```html
<p>
  每當阿尼掛掉的時候，屎蛋會說：
  <q
 cite="https://zh.wikipedia.org/zh-tw/%E8%82%AF%E5%B0%BC%C2%B7%E9%BA%A6%E5%85%8B%E5%BA%B7%E7%B1%B3%E5%85%8B">
    我的天啊！阿尼他被掛掉了！ </q
 >.
</p>
```

規範
--

| Specification |
| --- |
| HTML Standard # the-q-element |

瀏覽器相容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

參閱
--

* 針對長引文的 `<blockquote>` 元素
* 針對引用來源的 `<cite>` (en-US) 元素


<code> - HTML：超文本標記語言

<code>
======

*HTML Code 元素*（**<code>**）表現出電腦程式碼的片段。通常，它會由瀏覽器預設的 monospace 字體顯示出來。

| 內容分類 (en-US) | 流內容 (en-US)、段落式内容 (en-US)、捫及內容 |
| --- | --- |
| 允許內容 | 段落式内容 (en-US). |
| 標籤省略 | None, both the starting and ending tag are mandatory. |
| 允許父元素 | 任何允許段落式内容 (en-US)的元素。 |
| DOM 介面 | `HTMLElement` (en-US)。Gecko 1.9.2 (Firefox 4) 以前，Firefox 針對這個元素使用 `HTMLSpanElement` (en-US) 介面實做。 |

屬性
--

這個元素只支援全域屬性 (en-US)。

例子
--

```html
<p>Regular text. <code>This is code.</code> Regular text.</p>
```

### 結果

Regular text. `This is code.` Regular text.

註記
--

CSS 的 `code` 選擇器設定可以透過蓋過瀏覽器預設，而用戶個人設定可以蓋過 CSS 設定。

規範
--

| Specification |
| --- |
| HTML Standard # the-code-element |

瀏覽器相容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

See also
--------

* `<samp>` (en-US)
* `<kbd>` (en-US)
* `<command>` (deprecated)
* `<var>` (en-US)


<input type="submit"> - HTML：超文本標記語言

<input type="submit">
=====================

`<input>` (en-US) 元素的 **`"submit"`** 類型會被視為提交按鈕（submit button）——點選的話就能把表單提交到伺服器。

| **值** | 用作按鈕 label 的 `DOMString` |
| --- | --- |
| **事件** | `click` (en-US) |
| **常見的支援屬性** | `type` (en-US) 與 `value` (en-US) |
| **IDL 屬性** | `value` |
| **方法** | 無 |

值
-

`<input type="submit">` 元素的 `value` (en-US) 屬性會包含用作按鈕 label 的 `DOMString`。
```

<input type="submit" value="Submit to me" />
```

### 不指定 value 屬性

如果不指定 `value`，視瀏覽器不同，按鈕會是 *Submit*/*Submit Query*/*提交* 之類的預設值：
```

<input type="submit" />
```

使用提交按鈕
------

`<input type="submit">` 按鈕用於提交表單。如果想自訂按鈕、並透過 JavaScript 自訂其行為，你應該用 `<input type="button">` (en-US)、或更好的，`<button>` (en-US) 元素。

請記住，如果表單內只有一個按鈕元素（例如 `<button>My button</button>`）的話，瀏覽器會自動把它視為提交按鈕。你要在其他按鈕之外，明確宣告一個提交按鈕。

### 簡易的提交按鈕

我們要開始建立一個新的提交按鈕：

```html
<form>
  <div>
    <label for="example">Let's submit some text</label>
    <input id="example" type="text" name="text" />
  </div>
  <div>
    <input type="submit" value="Submit to me" />
  </div>
</form>
```

它會被渲染為：

試著在文字區塊內輸入些文字，接著提交表單。

提交時，送到伺服器的成對 name/value 資料會 be along the lines of `text=mytext`，視你在文字區塊內輸入了什麼。資料在哪裡並如何被送出，取決於 `<form>` 屬性和其他細節的設定：請參見傳送表單資料 (en-US)。

### 增加提交的快捷鍵

鍵盤快捷鍵，又稱熱鍵，能讓用戶透過鍵盤按鍵或組合觸發按鈕事件。要給提交按鈕添加鍵盤快捷鍵——如同 `<input>` (en-US) 那樣——你需要使用全域屬性 `accesskey` (en-US)。

下例之中，`s` 被指定為快捷鍵（you'll need to press `s` plus the particular modifier keys for your browser/OS combination; see `accesskey` (en-US) for a useful list of those）。

```html
<form>
  <div>
    <label for="example">Let's submit some text</label>
    <input id="example" type="text" name="text" />
  </div>
  <div>
    <input type="submit" value="Submit to me" accesskey="s" />
  </div>
</form>
```

**備註：** 上例的問題很明顯，就是用戶不知道要按什麼快捷鍵！在實際網站中，你要提供不干擾網站整體設計的快捷鍵資訊：像是提供易於訪問的連結，告訴用戶說網站的快捷鍵是什麼。

### 禁用與啟用提交按鈕

要禁用提交按鈕，就如同下例般指定全域屬性 `disabled` (en-US)：
```

<input type="submit" value="Disabled" disabled />
```

你可以在 run time 時藉由設定 `disabled` 的 `true` or `false` 來禁用或啟用提交按鈕。在 JavaScript 就看起來像 `btn.disabled = true`。

**備註：** 請參見 `<input type="button">` (en-US) 頁面以取得關於禁用/啟用提交按鈕的詳細資訊。

**備註：** Firefox 不若其他瀏覽器，它預設上會在 `<button>` (en-US) 跨網頁加載時保持動態禁用狀態。請用 `autocomplete` (en-US) 屬性控制這個功能。

驗證
--

提交按鈕不參與強制驗證：they have no real value to be constrained.

範例
--

上面已經有一些程式碼了。這裡也沒有什麼還能講的。

規範
--

| Specification |
| --- |
| HTML Standard # submit-button-state-(type=submit) |

瀏覽器相容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

參見
--

* 實做它的 `<input>` (en-US) 與 `HTMLInputElement` (en-US) 介面。
* `<button>` (en-US) 元素。


控制 HTML 表單中的拼字檢查功能 - HTML：超文本標記語言

控制 HTML 表單中的拼字檢查功能
==================

Firefox 2 新增了網頁表單文字輸入區的即時拼字檢查功能，使用者可以修改 about:config 來調整是否使用拼字檢查、是否同時檢查文字輸入**欄**等設定。預設值狀態下，Firefox 會檢查文字輸入區（text area）及 `designMode` 的文件，而不檢查文字輸入欄（text field）的拼字。

不過，總是會有些不見得適合預設值的情況，例如在某些用來編寫 HTML 等程式碼的輸入區中拼字檢查就變得煩人，或者有些網站也希望建議 Firefox 檢查某些特定文字輸入欄的拼字等等。

若網站對某特定 `<input>` 元素中的拼字檢查執行與否有所建議，則可使用 `spellcheck` 屬性。將此屬性設為 `true` 表建議檢查拼字，而設為 `false` 則相反。

若使用者將 `layout.spellcheckDefault` 直接設為 0 以關閉拼字檢查功能，那麼網站的建議就無效；若設為其他值，則會將網站建議列入考量。

舉例來說，你可以撰寫如下的 HTML 碼，建議 Firefox 檢查此文字輸入欄（`<input>` 元素）中的拼字：
```

<input type="text" size="50" spellcheck="true">
```

也可以撰寫這樣的 HTML 碼，建議 Firefox 不要檢查文字輸入區的拼字：
```

<textarea spellcheck="false"></textarea>
```

而 `designMode`（通常用於 HTML 所見即所得編輯）則可以在文件的 `<body>` 標籤中設定 `spellcheck` 屬性來控制拼字檢查。

`spellcheck` 屬性也可以放在 `<span>` 或 `<div>` 等其他標籤中，這麼一來在此元素下所有未另行設定 `spellcheck` 屬性的 `<input>` 元素將繼承此設定；而若皆未設定則使用者的決定將優先採行。

舉例而言：
```

<div spellcheck="true">
  <label>輸入英文句子： </label><input type="text" size="50">
  <br />
  <label>再打一句： </label><input type="text" size="50">
</div>
<br />
<label>最後一句： </label><input type="text" size="50">
```

在這段 HTML 程式碼中，頭兩個文字輸入區域將檢查拼字，第三個則不會。


data-* - HTML：超文本標記語言

data-\*
=======

**data-\*** 全域屬性 (en-US)構成一組稱作**自訂 data 屬性**的屬性。它能令 HTML 與其 DOM 擁有給腳本用的交換資訊。這種屬性設置的自訂資料，能透過元素的 `HTMLElement` (en-US) 介面而活用。`HTMLElement.dataset` (en-US) property 允許訪問它們。`*` 可以是任何遵循以下規則的 xml 名稱：

* 名字絕對不能以 `xml` 起頭，無論是否用於 xml、
* 名字絕對不能包含分號（`U+003A`）、
* 名字絕對不能包含大寫 `A` 到大小 `Z` 的拉丁字母。

請注意 `HTMLElement.dataset` (en-US) property 是個 `DOMStringMap` (en-US)，而自訂的 data 屬性名 *data-test-value* 因為所有的減號（`U+002D`）都會被消去、緊接著的第一個字母，會被取代為駝峰式（camelcase）名字，所以要透過 `HTMLElement.dataset.testValue` 或 `HTMLElement.dataset["testValue"]` 訪問。

### 用法

藉由增加 **data-\*** 屬性，即使是普通的 HTML 元素也能變成複雜而強大程式物件。例如說遊戲裡面的太空船精靈 能成為帶有 class (en-US) 與數個 data-\* 屬性的 `<img>` (en-US) 元素：
```

<img class="spaceship cruiserX3" src="shipX3.png"
  data-ship-id="324"   data-weapons="laserI laserII"   data-shields="72%"
  data-x="414354" data-y="85160" data-z="31940"
  onclick="spaceships[this.dataset.shipId].blasted()" />
```

（參見*這個網站*）

規範
--

| Specification |
| --- |
| HTML Standard # attr-data-\* |

瀏覽器相容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

參見
--

* 所有的全域屬性 (en-US)
* The `HTMLElement.dataset` (en-US) property that allows to access and modify these values.


閉包 - JavaScript

閉包
==

閉包（Closure）是函式以及該函式被宣告時所在的作用域環境（lexical environment）的組合。

語法作用域（Lexical scoping）
----------------------

思考這個例子：

```js
function init() {
  var name = "Mozilla"; // name 是個由 init 建立的局部變數
  function displayName() {
    // displayName() 是內部函式，一個閉包
    alert(name); // 使用了父函式宣告的變數
  }
  displayName();
}
init();
```

`init()` 建立了局部變數 `name` 與 `displayName()` 函式。`displayName()` 是個在 `init()` 內定義的內部函式，且只在該函式內做動。`displayName()` 自己並沒有局部變數，不過它可以訪問外面函式的變數、因而能取用在父函式宣告的變數 `name`。

運行這個程式碼並注意 `displayName()` 裡面的 `alert()` 宣告，它能顯示位於上一層的 `name` 變數。這實例在描述*語法作用域*碰上巢狀函式時，解析器（parser）會如何解讀（resolve）變數。「作用域」一詞，指的正是作用域環境在程式碼指定變數時，使用 location 來決定該變數用在哪裡的事情。巢狀函式的內部函式，能訪問在該函式作用域之外的變數。

閉包
--

再思考這個例子：

```js
function makeFunc() {
  var name = "Mozilla";
  function displayName() {
    alert(name);
  }
  return displayName;
}

var myFunc = makeFunc();
myFunc();
```

若你執行這個程式碼，它會與前例 `init()` 有相同結果：字串 Mozilla 會以 JavaScript alert 提示顯示出來。但箇中不同、且頗具趣味處，乃內部函式 `displayName()` 竟在外部函式執行前，從其回傳。

乍看之下，這段程式碼看來不大直覺：在某些程式語言，函式內的局部變數，只會在函式的執行期間存在。當 `makeFunc()` 執行完，你可能會預期 name 變數再也無法使用。但這段能照舊運行的程式碼表明了，在 JavaScript 並不是這樣做。

箇中理由和 JavaScript 函式的閉包有關。*閉包*為函式的組合、還有該宣告函式的作用域環境。這個環境包含閉包建立時，所有位於該作用域的區域變數。在這個例子中，`myFunc` 是 `displayName` 在 `makeFunc` 運行時所建立的實例（instance）參照。`displayName` 的實例保有了其作用域環境的參照，作用域裡則內含 `name` 變數。因此，在調用 `myFunc` 時，`name` 變數被保存、並能作它用。「Mozilla」一詞也因此傳給了 `alert`。

這裡有個更有趣的例子：`makeAdder` 函式：

```js
function makeAdder(x) {
  return function (y) {
    return x + y;
  };
}

var add5 = makeAdder(5);
var add10 = makeAdder(10);

console.log(add5(2)); // 7
console.log(add10(2)); // 12
```

在此，我們定義一個帶有單一參數 `x` 並回傳新函式的函式 `makeAdder(x)`。該新函式又帶有 `y` 參數並回傳了 `x` 與 `y` 的總和。

本質上 `makeAdder` 乃為函式工廠：它是個建立給定值、並與其參數求和之函式。上例中我們的函式工廠建立了兩個新函式：一個給參數加 5，另一個則是 10。

`add5` 與 `add10` 都是閉包。他們共享函式的定義，卻保有不同的環境：在 `add5` 的作用域環境，`x` 是 5。而在 `add10` 的作用域環境， `x` 則是 10。

實用的閉包
-----

閉包實用之處，在於能讓你把一些資料（透過作用域環境）與操控這些資料的函式相關聯。很明顯地，這與把一些資料（物件屬性）與一些方法的相關聯的物件導向程式設計（object-oriented programming）相似。

因此，在使用只含一個方法的物件之處，通常也可以使用閉包。

在 Web 中，試圖做這種事的情況還蠻普遍的。我們寫的大多數前端 JavaScript 程式碼屬於 event-based 的：我們定義了一些行為，接著把它與用戶觸發事件（例如點擊或按鍵）連結起來。程式碼通常會以 callback 的形式連結：也就是一個處理事件回應的函式。

例如，假設我們想在網頁上，加個能調整文字大小的按鈕。其中一個方法是用像素指定 `body` 元素的 font-size，接著透過相對的 em 單位，設置其他頁面的其他元素（如 headers）個大小：

```css
body {
  font-family: Helvetica, Arial, sans-serif;
  font-size: 12px;
}

h1 {
  font-size: 1.5em;
}

h2 {
  font-size: 1.2em;
}
```

我們的互動式文字大小按鈕，可以改變 `body` 元素的 `font-size` 屬性（property）並藉由相對單位令頁面其他元素接受相應調整。

以下是 JavaScript：

```js
function makeSizer(size) {
  return function () {
    document.body.style.fontSize = size + "px";
  };
}

var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);
```

`size12`、`size14`、`size16` 現在變成能調整字體大小到 12、14、與 16 像素的函式。而我們能如下例一般，把他們附加到按鈕上（本例為連結）：

```js
document.getElementById("size-12").onclick = size12;
document.getElementById("size-14").onclick = size14;
document.getElementById("size-16").onclick = size16;
```

```html
<a href="#" id="size-12">12</a>
<a href="#" id="size-14">14</a>
<a href="#" id="size-16">16</a>
```

使用閉包模擬私有方法
----------

諸如 Java 之類的程式語言，提供了私有方法宣告的能力，意味著它們只能被同一個 class 的其他方法呼叫。

JavaScript 並沒有的提供原生的方法完成這種事，不過它藉由閉包來模擬私有方法。私有方法不只能限制程式碼的存取，它還提供了強而有力的方式來管理全域命名空間，避免非必要的方法弄亂公開介面。

以下展示如何使用閉包來定義一個能夠訪問私有函式與變數的公開函式。這種閉包的用法稱為模組設計模式(module pattern)。

```js
var counter = (function () {
  var privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment: function () {
      changeBy(1);
    },
    decrement: function () {
      changeBy(-1);
    },
    value: function () {
      return privateCounter;
    },
  };
})();

console.log(counter.value()); // logs 0
counter.increment();
counter.increment();
console.log(counter.value()); // logs 2
counter.decrement();
console.log(counter.value()); // logs 1
```

上例的每個閉包，都有各自的環境。即使如此，我們依舊建立了一個，被三個函式共享的環境：`counter.increment`、`counter.decrement`、`counter.value`。

該作用域環境由匿名函式的 body 建立，之後立刻執行。作用域環境還包括兩個私有項（private item）：變數 `privateCounter` 與函式 `changeBy`。這些私有項，都不會在匿名函式外直接訪問。相反地，它們要透過由匿名包裝器（anonymous wrapper）回傳的公有函式訪問。

這三個公有函式，皆為共享同一個環境的閉包。由於 JavaScript 的語法作用域，它們都能訪問 `privateCounter` 變數與 `changeBy` 函式。

**備註：** 你應該也發現到我們定義了建立 counter 的匿名函式、而我們接著呼叫它，並給`counter` 變數指派了回傳值。我們也能在分離的變數 `makeCounter` 儲存函式並用其建立數個 counter。

```js
var makeCounter = function () {
  var privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment: function () {
      changeBy(1);
    },
    decrement: function () {
      changeBy(-1);
    },
    value: function () {
      return privateCounter;
    },
  };
};

var counter1 = makeCounter();
var counter2 = makeCounter();
alert(counter1.value()); /\* Alerts 0 \*/
counter1.increment();
counter1.increment();
alert(counter1.value()); /\* Alerts 2 \*/
counter1.decrement();
alert(counter1.value()); /\* Alerts 1 \*/
alert(counter2.value()); /\* Alerts 0 \*/
```

請注意 `counter1` 與 `counter2` 這兩個計數器是如何維護其獨立性的。每個閉包都以各自的閉包，在參照不同版本的 `privateCounter` 變數。每當呼叫其中一個計數器時，它會透過該變數的數值變更，改變語法作用域的環境。不過，在其中一個閉包的變數值改時，其他閉包的數值並不會受到影響。

**備註：** 使用這種方法的閉包，提供了一些與物件導向程式設計的益處，尤其是資料隱藏與封裝。

在迴圈建立閉包：一個常見錯誤
--------------

在 ECMAScript 2015 導入 `let` 前，迴圈內建立的閉包，常會發生問題。請思考以下的範例：

```html
<p id="help">Helpful notes will appear here</p>
<p>E-mail: <input type="text" id="email" name="email" /></p>
<p>Name: <input type="text" id="name" name="name" /></p>
<p>Age: <input type="text" id="age" name="age" /></p>
```

```js
function showHelp(help) {
  document.getElementById("help").innerHTML = help;
}

function setupHelp() {
  var helpText = [
    { id: "email", help: "Your e-mail address" },
    { id: "name", help: "Your full name" },
    { id: "age", help: "Your age (you must be over 16)" },
  ];

  for (var i = 0; i < helpText.length; i++) {
    var item = helpText[i];
    document.getElementById(item.id).onfocus = function () {
      showHelp(item.help);
    };
  }
}

setupHelp();
```

`helpText` 陣列定義了三個有用的提示，每個提示都和文件內的輸入字段 ID 相關連。迴圈透過這三個定義，依序針對相對應的幫助方法（help method）添加了 `onfocus` 事件。

若試著運行這段程式碼，你會發現它不若預期般運行：無論聚焦哪一段，訊息都是在顯示你的年齡。

之所以如此，是因為指派到 `onfocus` 的函式為閉包，他們組成函式的定義、並從 `setupHelp` 的作用域捕抓函式的環境。三個閉包都被建立起來，但他們共享同一個能改變數值變數（`item.help`）的作用域環境。`item.help` 值早在執行 `onfocus` 回呼時，就已經被決定了。也由於 course 裡面的迴圈在那時已經執行了，給三個閉包共享的變數物件 `item` 早已離開 `helpText` 清單的 pointing to the last entry。

其中一個解法是使用更多閉包，尤其要使用前述的函式工廠：

```js
function showHelp(help) {
  document.getElementById("help").innerHTML = help;
}

function makeHelpCallback(help) {
  return function () {
    showHelp(help);
  };
}

function setupHelp() {
  var helpText = [
    { id: "email", help: "Your e-mail address" },
    { id: "name", help: "Your full name" },
    { id: "age", help: "Your age (you must be over 16)" },
  ];

  for (var i = 0; i < helpText.length; i++) {
    var item = helpText[i];
    document.getElementById(item.id).onfocus = makeHelpCallback(item.help);
  }
}

setupHelp();
```

這次就如同預期般的運作了。與所有回調共享作用域環境相比，`makeHelpCallback` 給每個回調建立新的作用域環境，該環境的 `help` 參照到 `helpText` 陣列的對應字串。

用匿名閉包來實做的另一種方法是：

```js
function showHelp(help) {
  document.getElementById("help").innerHTML = help;
}

function setupHelp() {
  var helpText = [
    { id: "email", help: "Your e-mail address" },
    { id: "name", help: "Your full name" },
    { id: "age", help: "Your age (you must be over 16)" },
  ];

  for (var i = 0; i < helpText.length; i++) {
    (function () {
      var item = helpText[i];
      document.getElementById(item.id).onfocus = function () {
        showHelp(item.help);
      };
    })(); // Immediate event listener attachment with the current value of item (preserved until iteration).
  }
}

setupHelp();
```

如果你不想用更多閉包的話，你可以使用 ES2015 的 `let` 關鍵字：

```js
function showHelp(help) {
  document.getElementById("help").innerHTML = help;
}

function setupHelp() {
  var helpText = [
    { id: "email", help: "Your e-mail address" },
    { id: "name", help: "Your full name" },
    { id: "age", help: "Your age (you must be over 16)" },
  ];

  for (var i = 0; i < helpText.length; i++) {
    let item = helpText[i];
    document.getElementById(item.id).onfocus = function () {
      showHelp(item.help);
    };
  }
}

setupHelp();
```

在這裡，我們用了 `let` 而不是 `var`，所以每個閉包都會與每個 block-scoped 變數綁定，因而能在不用更多閉包的情況下完美運行。

性能考量
----

如果指定的任務無須使用閉包的話，在其他函式內建立不必要的函式並不明智，因為從速度和記憶體角度而言，它都會影響腳本性能。

例如說，當我們建立了新的 object/class 時候，方法通常要和物件的原型（prototype）相關聯，而不是定義到物件的建構子（constructor）── 這是因為每當建構子被呼叫的時候，方法都會重新分配（也就是說，它每次都在建立物件）。

思考一下這個例子：

```js
function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
  this.getName = function () {
    return this.name;
  };

  this.getMessage = function () {
    return this.message;
  };
}
```

上面的程式碼並未利用閉包的益處，所以，可以改成這個樣子：

```js
function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
}
MyObject.prototype = {
  getName: function () {
    return this.name;
  },
  getMessage: function () {
    return this.message;
  },
};
```

但我們不建議重新定義原型，因此這個附加到現有原型的例子更佳：

```js
function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
}
MyObject.prototype.getName = function () {
  return this.name;
};
MyObject.prototype.getMessage = function () {
  return this.message;
};
```

以上的程式碼，可以寫得如同下例般簡潔：

```js
function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
}
(function () {
  this.getName = function () {
    return this.name;
  };
  this.getMessage = function () {
    return this.message;
  };
}).call(MyObject.prototype);
```

在前例中，所有物件可共享繼承的原型，物件創立時也無須每次都定義方法。詳細資料請參見深入了解物件模型 (en-US)。


記憶體管理 - JavaScript

記憶體管理
=====

介紹
--

像 C 語言一樣低階的語言，都有著如 malloc() 跟 free() 的低階函式記憶體控管權限。另一方面，當 JavaScript 建立事物(如物件、字串等)時會分配空間給值且自動釋放不再使用的值。後者的流程稱作為回收機制。這個自動化的回收流程是一個混亂的根源，它會使 JavaScript 的開發者 (或者其他高階語言的開發者) 產生可以不須理會「記憶體管理」的錯誤認知。

記憶體生命週期
-------

不論是哪種程式語言，記憶體生命週期(運作方式)幾乎總是一樣：

1. 配置你程式需要的記憶體空間
2. 使用配置到的記憶體空間(讀，寫)
3. 當不再使用時釋放已被配置的記憶體空間

在所有語言中，第二點的(運作方式)是確定的。第一點以及最後一點在低階語言中是確定的，但是在高階語言如 JavaScript 則通常是不明確的。

### JavaScript 的記憶體配置

#### 值的初始化

為了不讓開發者對配置感到困擾，JavaScript 會在宣告值的同時完成記憶體配置

```js
var n = 123; // 配置一記憶體空間給數字
var s = "azerty"; // 配置記憶體空間給字串

var o = {
  a: 1,
  b: null,
}; //配置記憶體空間給內含值的物件

// (像物件一樣) 分配記憶體給一個陣列
// 該陣列包含一些值
var a = [1, null, "abra"];

function f(a) {
  return a + 2;
} // 分配記憶體給一個函式 (一個可呼叫的物件)

// function expressions also allocate an object
someElement.addEventListener(
  "click",
  function () {
    someElement.style.backgroundColor = "blue";
  },
  false,
);
```

#### 藉由函式呼叫來配置

有些函式呼叫後產生物件配置。

```js
var d = new Date(); // 配置一個日期物件

var e = document.createElement("div"); // 配置一個 DOM 物件
```

有些方法配置新的值或物件：

```js
var s = "azerty";
var s2 = s.substr(0, 3); // s2 是一個新字串
// 因為字串是一種不可變的值，
// JavaScript 會決定不分配新的記憶體，
// 只儲存 0 到 3 這個範圍的字元。

var a = ["ouais ouais", "nan nan"];
var a2 = ["generation", "nan nan"];
var a3 = a.concat(a2);
// 一個有四個元素的新陣列
// 將 a 與 a2 的元素串聯起來
```

### 值的使用

基本上使用值表示對已被配置的記憶體做讀寫。可藉由讀取或寫入變數的值或一個物件特性或甚至傳一個參數到函數中來完成此事。

### 釋放不再使用的記憶體

當我們談論到記憶體管理，問題通常出現在這個階段。最困難的工作是尋找「已不再被使用的記憶體配置空間」。

低階語言需要開發者自己決定，當程序執行道某個地方時，是否有已被分配的記憶體不再需要。並手動將其釋放。

高階的語言 (e.g. JavaScript) 有一個叫作垃圾回收器(garbage collector) 的系統，他的工作是追蹤記憶體分配的使用情況，以便自動釋放一些不再使用的記憶體空間。但這個垃圾回收器只是「儘量」做到自動釋放記憶體空間，因為判斷記憶體空間是否要繼續使用，這件事是「不可判定(undecidable)」的（不能用演算法來解決）。

回收機制
----

如上所述，要自動判定記憶體空間「不再使用」是不可能的。因此，回收機制只是實作一個大部份特定情況下的解決辦法。本章節將解釋主要的垃圾回收機制演算法與他們的限制。

### 參考

回收機制的演算法主要概念是參考(reference)概念。在記憶體管理的上下文中(context)，如果一個物件可以訪問到另一個物件(無論是隱式或顯式)，即稱為該物件參考另一個物件。例如：JavaScript 的物件都有參考該物件的原型(prototype) (隱式參考) 以及該物件的屬性值 (顯式參考)。

在原型鏈的情況下，物件的概念擴展到比普通的 JavasScript 物件更廣，這個概念還包含函數作用域 (function scopes)（或全局詞法作用域 (global lexical scope) ）。

### Reference-counting garbage collection

這是一個最務實的垃圾回收演算法。 這個演算法將原本「這個物件再也不會被使用」的廣泛定義縮減到「沒有其他任何物件參考它」。如果一個物件不在被任何物件參考，它將被視為可回收記憶體的垃圾。

#### 範例

```js
var o = {
  a: {
    b: 2,
  },
};
// 兩個物件被創造出來。其中一個物件(b)被視為另一個物件(a)的屬性，並且被該物件(a)參考
// 另一個物件(a)被分配給一個變數(o)，並且被變數(o)參考
// 很明顯的，沒有任何物件應該被回收

var o2 = o; // 另外創造第二個變數(o2)
// 他參考一個物件
o = 1; // 現在，物件 o 有另一個不同的參考
// 而原本在物件 o 的物件，則被物件 o2 參考

var oa = o2.a; // 變數 oa 參考物件 a
// 物件 a 有兩個物件參考到它，一個是 o2，並且它是 o2 的屬性
// 另一個是變數 oa

o2 = "yo"; // 現在 o2 變成 'yo'
// 原本應該沒有任何物件參考到 a ，因此它應該被回收
// 但 a 仍然被變數 oa 參考，因此它逃過被回收的命運

oa = null; // 現在把 oa 變成 null
// 因為沒有任何物件與變數參考 a ，因此他可以被回收
```

#### 限制：循環

當涉及到循環時有一個限制。在下面的例子中，創造兩個物件並相互參考，從而製作一個循環。當呼叫函式時，他們應該超出作用範圍，因此他們實際上是無用且可釋放。但垃圾回收參考計數演算法會認為，兩個物件都至少被參考一次，因此兩個都是不能被回收的。

```js
function f() {
  var o = {};
  var o2 = {};
  o.a = o2; // o 參考 o2
  o2.a = o; // o2 參考 o

  return "azerty";
}

f();
```

#### 真實案例

Internet Explorer 6 和 7 有一個 DOM 物件的垃圾回收參考計數。循環是一個造成記憶體洩漏(memory leaks)的常見問題：

```js
var div;
window.onload = function () {
  div = document.getElementById("myDivElement");
  div.circularReference = div;
  div.lotsOfData = new Array(10000).join("\*");
};
```

在上面的例子中，DOM 元素「myDivElement」在「circularReference」屬性中有一個循環參考。如果該屬性未被直接刪除或為零，垃圾回收參考計數將視其始終至少有一個參考保持不變，並將 DOM 元素保存在記憶體裡，即使它已從 DOM 樹中移除。如果該 DOM 元素擁有大量資料（如同上面的例子中的 lotsOfData 屬性），則此資料所佔用的記憶體將永遠不會釋放。

### 標記和清理演算法

這個演算法將「這個物件再也不會被使用」的定義縮減到「這個物件不可到達」。

這個演算法假設一組物件，其名為「根 (roots)」（在 JavaScript 中，根(roots)是全局物件）。垃圾回收器定期從這些根開始，尋訪所有被根參考的物件與那些物件參考的物件。垃圾回收器會回收那些沒有被尋訪的到物件。

這個演算法比前一個演算法更好，因為一個不被任何物件參考的物件，一定無法從根被尋訪到。然而，一個無法從根被尋訪到的物件，不一定不被任何物件參考，可以在循環的例子中發現這件事。

截至 2012 年，所有現代瀏覽器都使用標記和清理的垃圾回收器。 在過去的幾年裡，JavaScript 垃圾回收領域的所有改進都是對這個演算法的實作與改進，但並未改進垃圾回收演算法本身，也沒有縮減垃圾的定義 「這個物件再也不會被使用」。

#### 循環不再是一個問題

在上面的第一個範例中，函式呼叫返回後，那兩個物件不再被全局物件可以尋訪到的物件參考。 因此，它們將被垃圾回收器發現，視為不可達到的。

第二個範例也是一樣的。 一旦 div 和它的處理程序從根本上無法尋訪，它們都會被垃圾回收掉，儘管他們自身相互參考。

#### 限制：物件必須明顯的不可達到

雖然有這個限制存在，但在實務卻很少發生。這也是幾乎沒有人關心這件事的原因。

Node.js
-------

Node.js offers additional options and tools for configuring and debugging memory issues that may not be available for JavaScript executed within a browser environment.

#### V8 Engine Flags

The max amount of available heap memory can be increased with a flag:

`node --max-old-space-size=6000 index.js`

We can also expose the garbage collector for debugging memory issues using a flag and the Chrome Debugger:

```shell
node --expose-gc --inspect index.js
```

參見
--

* IBM article on "Memory leak patterns in JavaScript" (2007)
* Kangax article on how to register event handler and avoid memory leaks (2010)
* Performance


JavaScript 技術概觀 - JavaScript

JavaScript 技術概觀
===============

簡介
--

在網頁中，HTML 存放網頁的內容與格式，CSS 註記內容的呈現樣式；而 JavaScript 則用來產生豐富的效果或網頁應用程式。然而，在網頁瀏覽器中通稱的「JavaScript」是由眾多截然不同的要素所構成。其中之一是核心語言（ECMAScript），另一個則是 DOM（文件物件模型，Document Object Model）。

JavaScript 的核心語言（ECMAScript）
----------------------------

JavaScript 的核心語言是由 ECMA TC-39 委員會統一標準，並且命名為 ECMAScript 。隨著最新版本的規格 ECMAScript 5 在 2011 年 3 月推出，大部分最新的網頁瀏覽器實作了 ECMAScript 3 與部分的 ECMAScript 5。

### ECMAScript 包含了什麼？

ECMAScript 主要定義了以下的內容：

* 語言的語法（解析規則，關鍵字，流程控制，物件的初始化… ）
* 錯誤處理機制 （throw、 try/catch、 允許自訂錯誤型別）
* 型態（布林代數、數字、字串、函數、物件…）
* 全域物件。在瀏覽器環境中，此物件即為 window 物件。許多函數皆包含在此物件下（parseInt, parseFloat, decodeURI, encodeURI…）
* 一種基於原型（Prototyping）的繼承機制
* 內建的物件與函數 （JSON、Math、Array.prototype 方法、物件自識方法…）
* 嚴格模式（Strict mode）
### 瀏覽器支援

根據以前的經驗，ECMAScript 的功能都有良好且互通的支援。截至 2011 年 6 月，ECMAScript 5 的支援在不同瀏覽器實作之間存在差異。有些文件整理了各瀏覽器對 ECMAScript 5 的支援情形。

DOM（文件物件模型）
-----------

### WebID

WebIDL 規格書 是 DOM 技術與 ECMAScript 之間相互黏合的基礎。

### DOM 核心

W3C 統一規範了文件物件模型的核心部分。它定義了無關語言而將 HTML 與 XML 文件抽象化為物件的介面，以及對抽象化物件進行處理的機制。在 DOM 的定義中包含：

* DOM core 中的文件結構、樹狀模型，DOM 事件結構：Node, Element, DocumentFragment, Document, DOMImplementation, Event, EventTarget, …
* DOM events 中對 DOM 事件結構（DOM Event Architecture）和特定事件的廣義定義。
* 其他如 DOM Traversal 和 DOM Range。

以 ECMAScript 的觀點來看，DOM 規範所定義的物件被稱為宿主物件（Host objects） 。

### HTML DOM

網頁標記語言 HTML 是依據 DOM 來規格化。在 DOM 核心中，定義了虛擬概念的分層，而 HTML 也定義了元素的意義。 HTML DOM 包含了 HTML 元素的 `className` 屬性與諸如 `document.body` (en-US) 的 API。

HTML 的規格書也定義了在文件上的一些限制；例如，他要求所有 `ul` 元素（代表無順序編號的清單）的子元素必須是 `li 元素（代表清單`中的項目）。而未在標準中定義的元素和屬性通常在規格中被禁用。

其他重要的 API
---------

* setTimeout 和 setInterval 函數首次納入 HTML 標準中的 Window 介面
* XMLHttpRequest：允許傳送非同步 HTTP 請求的 API。
* CSS Object Model：CSSOM 用來將 CSS 的規則轉換為物件。
* WebWorkers：允許平行計算的 API。
* WebSockets：允許低階雙向溝通的 API。
* Canvas 2D Context：canvas 元素的繪圖 API。
Shell
-----

A JavaScript shell allows you to quickly test snippets of JavaScript code without having to reload a web page. They are extremely useful for developing and debugging code.

### 獨立的 JavaScript shell

下列的 JavaScript shell 都是獨立的環境，就像 perl 或是 python 一樣。

* Node.js - Node.js 是一個可以快速建置，可擴充的網路應用程式
* GraalJS - A high performance implementation of the JavaScript programming language. Built on the GraalVM by Oracle Labs.
* ShellJS - Node.js 裡可攜式的 Unix shell 命令
基於瀏覽器的 JavaScript shell
-----------------------

下列 JavaScript shell 需要 Mozilla 才能運作

* Firebug - Firefox 的開發者工具，同樣包函主控台
* Babel REPL - 以瀏覽器為基準的 REPL，主要用來實驗未來的 JavaScript
* TypeScript playground — A browser-based playground for experimenting both new JavaScript features (via the tsc compiler) and TypeScript syntax.
瀏覽器支援
-----

每個網頁開發者都曾經體會過一團混亂的 DOM 支援狀況。某些功能上不同瀏覽器的支援可能差異很大，因為過去重要的 DOM 功能都沒有明確的規範，進而使不同網頁瀏覽器對相同的使用情境實踐了互不相容的功能（像是 Internet Explorer 的事件模型）。最近（截至 2011 年 6 月），由 W3C 與（特別是）WHATWG 對舊功能重新進行詳盡的定義以提升互通性成為了趨勢 。為了跟進這個趨勢，許多網頁瀏覽器也根據這些新規格改進它們的實作。

雖然並未完全可靠，使用 JavaScript 函式庫是解決跨瀏覽器相容問題的最普遍方式。 這些函式庫將 DOM 的功能抽象化，確保其 API 在不同的瀏覽器中能以相似的方式運作。廣為被使用的框架包括 jQuery、prototype 和 YUI。


JavaScript 指南 - JavaScript

JavaScript 指南
=============

JavaScript 指南會讓你了解如何使用 JavaScript 並給你這個語言的概觀。若你需要語言功能詳細資訊請參考 JavaScript 參考文件。

章節
--

本指南區分成以下數個章節：

簡介
--

概述：簡介

* 關於本指南
* 關於 JavaScript
* JavaScript 與 Java
* ECMAScript
* 工具
* Hello World
語法與型別
-----

概述：語法與型別

* 基礎語法 & 註解
* 宣告
* 變數範圍
* 變數提升（Hoisting）
* 資料結構與型態
* 字面值
流程控制與錯誤處理
---------

概述：流程控制與錯誤處理

* `if...else`
* `switch`
* `try`/`catch`/`throw`
* Error 物件
* Promises
迴圈與迭代
-----

概述：迴圈與迭代

* `for`
* `while`
* `do...while`
* `break`
* `continue`
* `for..in`
* `for..of`
函數
--

概述：函數

* 定義函數
* 呼叫函數
* 函數範圍
* 閉包（Closure）
* 參數值
* 參數
* 箭頭函數
運算式與運算子
-------

概述：運算式與運算子

* 賦值
* 比較
* 算數運算子
* 位元
* 邏輯運算子
* 條件（三元）運算子
數字與日期
-----

概述：數字與日期

* 數字書寫法
* `Number` 物件
* `Math` 物件
* `Date` 物件
文字格式
----

概述：文字格式 (en-US)

* 字串書寫法 (en-US)
* `String` 物件 (en-US)
* 模板書寫法 (en-US)
* 國際化 (en-US)
* 正規表示法
具索引的集合
------

概述：文字格式 (en-US)

* 陣列 (en-US)
* 型態化陣列 (en-US)
具鍵值的集合
------

概述：具鍵值的集合 (en-US)

* `Map` (en-US)
* `WeakMap` (en-US)
* `Set` (en-US)
* `WeakSet` (en-US)
使用物件
----

概述：使用物件 (en-US)

* 物件與屬性 (en-US)
* 建立物件 (en-US)
* 定義方法 (en-US)
* 取值器與設值器 (en-US)
深入了解物件模型
--------

概述：深入了解物件模型 (en-US)

* 以原形（Prototype）為基礎的 OOP (en-US)
* 建立物件層級 (en-US)
* 繼承 (en-US)
迭代器與產生器
-------

概述：迭代器與產生器 (en-US)

* 迭代器（Iterator） (en-US)
* 可迭代型態（Iterable） (en-US)
* 產生器（Generator） (en-US)
Meta 程式設計
---------

概述：Meta 程式設計 (en-US)

* `Proxy` (en-US)
* Handler 與 Trap (en-US)
* Revocable Proxy (en-US)
* `Reflect` (en-US)
* 次頁 »


相等比較 - JavaScript

相等比較
====

在 ES2015，有四個相等比較方法：

* 一般相等 (`==`)
* 嚴格相等 (`===`)：被用於 `Array.prototype.indexOf`、 `Array.prototype.lastIndexOf` 和 `case`-matching
* 零值相等：被用於 `%TypedArray%` 和 `ArrayBuffer` 建構子，以及 `Map` 和 `Set` 運算子，還有將在 ES2016 新增的 `String.prototype.includes。`
* 同值相等： 用在除上面提及的所有情況。

JavaScript 提供三種不同的值比較運算操作：

* 嚴格相等 (或稱 "三等於"、"全等") 使用 === (en-US)
* 一般相等 ("雙等於") 使用 == (en-US)
* 還有 `Object.is` (en-US) (ECMAScript 2015 新加入)

要用哪個操作取決於你要哪種類型的比較。

簡單來說，一般相等會將型別一致化後比較；嚴格相等則不會（也就是說若型別不同，就會回傳 fasle）；`Object.is` 會和嚴格相等做同樣的事，但會將 `NaN`、`-0` 和 `+0` 獨立處理，因此這三個不會相等，而 `Object.is(NaN, NaN)` 則會回傳 true 。（用一般相等或嚴格相等比較兩個 `NaN` 時會回傳 `false` ，因為 IEEE 754 如此規範。） 切記，這三種判斷必須考慮原型，因為他們在設計上不被考慮為相等。對於任何非原型物件 x、y，即使他們有著相同結構，但如果是不同物件，比較就會是 false。

嚴格相等（`===`）
-----------

嚴格相等比較兩個值，而被比較的兩個值都不會轉換成其他型別。如果值是不同型別，就會被視為不相等。如果兩值型別相同但不是數字，若值相同，則為相等。此外，如果兩個值皆為數字，只要他們是 NaN 以外的同一值，或者 +0 和 -0，則為相等。

```js
var num = 0;
var obj = new String("0");
var str = "0";

console.log(num === num); // true
console.log(obj === obj); // true
console.log(str === str); // true

console.log(num === obj); // false
console.log(num === str); // false
console.log(obj === str); // false
console.log(null === undefined); // false
console.log(obj === null); // false
console.log(obj === undefined); // false
```

嚴格比較適合在絕大多數情況下使用。對於所有非數字的值，嚴格比較就如字面：一個值只相等於自己。而數字則使用稍微不同的方式：第一種情況是浮點數 0 同時為正和負，在解決某些數學問題時，`+0` 和 `-0` 是不同的，但在大部分情況下我們不需要考慮這種情境，因此嚴格比較將他們視為相同的。第二種情況是非數字，`NaN`，用來表示某些定義不明確的數學問題的解， 例如：負無窮加正無窮，嚴格比較認為 `NaN` 不等於任何值，包含他本身。（`(x !== x)`只有在 `x` 是 `NaN`時會是 `true`。）

一般相等（==）
--------

一般相等會*先將*比較值轉換成同型別後比較。轉換後（可能一個或兩個都被轉換），接著進行的幾乎和嚴格比較（`===`）一樣。 一般相等會*對稱*： `A == B` 等同 `B == A` ，無論 `A` 和 `B` 是什麼。（除了型別轉換的順序）

不同型別的一般相等運作如下表：

|  | 比較值 B |
| --- | --- |
|  |  | Undefined | Null | Number | String | Boolean | Object |
| 比較值 A | Undefined | `true` | `true` | `false` | `false` | `false` | `false` |
| Null | `true` | `true` | `false` | `false` | `false` | `false` |
| Number | `false` | `false` | `A === B` | `A === ToNumber(B)` | `A === ToNumber(B)` | `A == ToPrimitive(B)` |
| String | `false` | `false` | `ToNumber(A) === B` | `A === B` | `ToNumber(A) === ToNumber(B)` | `A == ToPrimitive(B)` |
| Boolean | `false` | `false` | `ToNumber(A) === B` | `ToNumber(A) === ToNumber(B)` | `A === B` | `ToNumber(A) == ToPrimitive(B)` |
| Object | `false` | `false` | `ToPrimitive(A) == B` | `ToPrimitive(A) == B` | `ToPrimitive(A) == ToNumber(B)` | `A === B` |

根據上表， `ToNumber(A)` 嘗試在比較前轉換成一個數字。 這等同 `+A` （單 + 運算子）。`ToPrimitive(A)` 嘗試從物件轉換成原生值，透過嘗試對 A 使用 `A.toString` 和 `A.valueOf` 方法。

一般來說，根據 ECMAScript 規範，所有物件應該不等於 `undefined` 和 `null`。但大多數瀏覽器允許很小部分的物件（尤其是所有頁面的 `document.all` 物件）在某些情況下*當成* `undefined`。一般相等是其中一種：當 A 是個被*模擬* 成 `undefined` 的物件，`null == A` 和 `undefined == A` 會是 true。而在其他情況下物件不會等同於 `undefined` 或 `null。`

```js
var num = 0;
var obj = new String("0");
var str = "0";

console.log(num == num); // true
console.log(obj == obj); // true
console.log(str == str); // true

console.log(num == obj); // true
console.log(num == str); // true
console.log(obj == str); // true
console.log(null == undefined); // true

// 除了少數情況，這兩個應該是 false。
console.log(obj == null);
console.log(obj == undefined);
```

部分開發者認為最好別用一般相等。嚴格比較更容易預測，且因為不必轉型，因此效率更好。

同值相等
----

同值相等解決了最後一個情況：比較兩個值是否*功能相同* 。（這裡用了 Liskov 替換原則（英） 為例）當試圖修改一個不可變的屬性：

```js
// 新增一個不可變 NEGATIVE\_ZERO 屬性到 Number 原型。
Object.defineProperty(Number, "NEGATIVE\_ZERO", {
  value: -0,
  writable: false,
  configurable: false,
  enumerable: false,
});

function attemptMutation(v) {
  Object.defineProperty(Number, "NEGATIVE\_ZERO", { value: v });
}
```

當修改一個不可變屬性時， `Object.defineProperty` 會出現例外，但若沒有真的要求修改，就沒事。如果 `v` 是 `-0`，就不會有修改，也就不會有錯誤出現。但若 `v` 是 `+0`，`Number.NEGATIVE_ZERO` 不再擁有自己的不可變屬性。在內部，當一個不可變屬性被重新定義，新的值會用同值相等和原值比較。

`Object.is` (en-US) 方法提供同值相等比較。

零值相等
----

和同值相等一樣，但將 `+0` 和 `-0` 視為相同。

一般相等、嚴格相等和同值相等的規範
-----------------

在 ES5，一般相等 `==` (en-US) 在 Section 11.9.3, The Abstract Equality Algorithm 中規範。嚴格相等 `===` (en-US) 在 11.9.6, The Strict Equality Algorithm。（可以看看，這很簡短且可讀。註：先讀嚴格相等。）ES5 也在 Section 9.12, The SameValue Algorithm 規範 JS 引擎的行為。他幾乎和嚴格相等一樣，除了 11.9.6.4 和 9.12.4 在處理 `Number` 時的不同。ES2015 簡短的提出了 `Object.is。`

我們可以發現在 11.9.6.1 中，除了 11.9.6.1 規範型別檢查，嚴格相等規範是從屬於一般相等規範，因為 11.9.6.2–7 和 11.9.3.1.a–f 相應。

理解相等比較模型
--------

ES2015 以後，你或許會將雙等於和三等於解讀成是彼此的「加強版」。比如，有人或許會說雙等於是三等於的延伸版本，因為前者做的事情和後者事情一模一樣，只差在運算元的型別轉換。舉例來說，`6 == "6"` （又或者說，有人可能會講說雙等於是基底，而三等於是加強版，因為它要求兩個運算元是同型別，所以它多了一個限制。至於哪個是較好的理解模型，取決於你的觀點。

儘管如此，這個思考內建相同運算子的方法，並非是延伸 ES2015 中的 `Object.is` (en-US) 方法。 `Object.is` (en-US) 不是單純地比雙等號「更寬鬆」或比三等號「更嚴謹」，也不適合將其放在兩者之間（即，比雙等號嚴謹，但較三等號寬鬆）。我們可以從下方的比較表格看到，一切是起源於 `Object.is` (en-US) 可以處理 `NaN` 的比較運算。要注意的是，如果 `Object.is(NaN, NaN)` 的運算結果是 `false` ，我們就可以因為它區分 `-0` 和 `+0` 的結果，使用寬鬆和嚴謹的範疇來界定它是比三等號更嚴謹的那一區段。然而，區別 `NaN` 的方式並不確實。Unfortunately, `Object.is` (en-US) simply has to be thought of in terms of its specific characteristics, rather than its looseness or strictness with regard to the equality operators.

| x | y | `==` | `===` | `Object.is` |
| --- | --- | --- | --- | --- |
| `undefined` | `undefined` | `true` | `true` | `true` |
| `null` | `null` | `true` | `true` | `true` |
| `true` | `true` | `true` | `true` | `true` |
| `false` | `false` | `true` | `true` | `true` |
| `"foo"` | `"foo"` | `true` | `true` | `true` |
| `{ foo: "bar" }` | `x` | `true` | `true` | `true` |
| `0` | `0` | `true` | `true` | `true` |
| `+0` | `-0` | `true` | `true` | `false` |
| `0` | `false` | `true` | `false` | `false` |
| `""` | `false` | `true` | `false` | `false` |
| `""` | `0` | `true` | `false` | `false` |
| `"0"` | `0` | `true` | `false` | `false` |
| `"17"` | `17` | `true` | `false` | `false` |
| `[1,2]` | `"1,2"` | `true` | `false` | `false` |
| `new String("foo")` | `"foo"` | `true` | `false` | `false` |
| `null` | `undefined` | `true` | `false` | `false` |
| `null` | `false` | `false` | `false` | `false` |
| `undefined` | `false` | `false` | `false` | `false` |
| `{ foo: "bar" }` | `{ foo: "bar" }` | `false` | `false` | `false` |
| `new String("foo")` | `new String("foo")` | `false` | `false` | `false` |
| `0` | `null` | `false` | `false` | `false` |
| `0` | `NaN` | `false` | `false` | `false` |
| `"foo"` | `NaN` | `false` | `false` | `false` |
| `NaN` | `NaN` | `false` | `false` | `true` |

When to use `Object.is` (en-US) versus triple equals
----------------------------------------------------

Aside from the way it treats `NaN`, generally, the only time `Object.is` (en-US)'s special behavior towards zeros is likely to be of interest is in the pursuit of certain meta-programming schemes, especially regarding property descriptors when it is desirable for your work to mirror some of the characteristics of `Object.defineProperty`. If your use case does not require this, it is suggested to avoid `Object.is` (en-US) and use `===` (en-US) instead. Even if your requirements involve having comparisons between two `NaN` values evaluate to `true`, generally it is easier to special-case the `NaN` checks (using the `isNaN` method available from previous versions of ECMAScript) than it is to work out how surrounding computations might affect the sign of any zeros you encounter in your comparison.

Here's an in-exhaustive list of built-in methods and operators that might cause a distinction between `-0` and `+0` to manifest itself in your code:

`- (unary negation)` (en-US)

It's obvious that negating `0` produces `-0`. But the abstraction of an expression can cause `-0` to creep in when you don't realize it. For example, consider:`js let stoppingForce = obj.mass * -obj.velocity` If `obj.velocity` is `0` (or computes to `0`), a `-0` is introduced at that place and propogates out into `stoppingForce`.

`Math.atan2` (en-US),
 `Math.ceil`,
 `Math.pow` (en-US),
 `Math.round`

It's possible for a `-0` to be introduced into an expression as a return value of these methods in some cases, even when no `-0` exists as one of the parameters. E.g., using `Math.pow` (en-US) to raise `-Infinity` to the power of any negative, odd exponent evaluates to `-0`. Refer to the documentation for the individual methods.

`Math.floor`,
 `Math.max` (en-US),
 `Math.min` (en-US),
 `Math.sin` (en-US),
 `Math.sqrt` (en-US),
 `Math.tan` (en-US)

It's possible to get a `-0` return value out of these methods in some cases where a `-0` exists as one of the parameters. E.g., `Math.min(-0, +0)` evalutes to `-0`. Refer to the documentation for the individual methods.

`~` (en-US),
 `<<` (en-US),
 `>>` (en-US)

Each of these operators uses the ToInt32 algorithm internally. Since there is only one representation for 0 in the internal 32-bit integer type, `-0` will not survive a round trip after an inverse operation. E.g., both `Object.is(~~(-0), -0)` and `Object.is(-0 << 2 >> 2, -0)` evaluate to `false`.

Relying on `Object.is` (en-US) when the signedness of zeros is not taken into account can be hazardous. Of course, when the intent is to distinguish between `-0` and `+0`, it does exactly what's desired.

See also
--------

* JS Comparison Table


JavaScript 的資料型別與資料結構 - JavaScript

JavaScript 的資料型別與資料結構
=====================

程式語言都具有資料結構，但在不同的程式語言之間會有些差異。這裡將列出可以在 JavaScript 中使用的資料結構，以及它們的特性；它們可以用來構成其他的資料結構。如果可以的話，會標示與其他語言不同的地方。

動態型別
----

JavaScript 是弱型別，也能說是動態的程式語言。這代表你不必特別宣告變數的型別。程式在運作時，型別會自動轉換。這也代表你可以以不同的型別使用同一個變數。

```js
var foo = 42; // foo 目前是數字
var foo = "bar"; // foo 目前是字串
var foo = true; // foo 目前是布林值
```

資料型別
----

最新的 ECMAScript 標準定義了七種資料型別：

* 有六種資料型別是原始型別 (en-US)：
    + Boolean
    + Null
    + Undefined (en-US)
    + Number
    + BigInt (en-US)
    + String (en-US)
    + Symbol (en-US)（於 ECMAScript 6 新定義）
* 另外還有 Object
原始值
---

除了物件以外的所有值，都是原始定義的值（值意味著不能被改變）。例如與 C 不同的地方，就是字串是不變的。我們引用這些類型的值為 primitive values（原始值）。

### 布林型別

布林（Boolean）代表了有兩個值的邏輯實體：`true` 與 `false`。

### Null 型別

Null 型別只有一個值：`null`。請參見 `null` (en-US) 與 Null。

### 未定義型別

一個沒有被定義的變數有 `undefined` 值。請參見 `undefined` (en-US) 與 Undefined (en-US)。

### 數字型別

根據 ECMAScript 標準，數字型別只有一種：雙精度 64 位元二進制格式 IEEE 754 值（在 -(2^53 -1) and 2^53 -1 之間的數字）。**而整數並沒有指定的型別**。數字除了能代表浮點數以外，還有三個符號值：`+Infinity`、`-Infinity`、`NaN`（not-a-number，非數字）。

要檢查在 `+/-Infinity` 內可用的最大值或最小值，可以用 `Number.MAX_VALUE` (en-US) 或 `Number.MIN_VALUE` (en-US) 常數。從 ECMAScript 6 開始，也可以透過 `Number.isSafeInteger()` (en-US)、`Number.MAX_SAFE_INTEGER` (en-US)、`Number.MIN_SAFE_INTEGER` (en-US) 檢查某數字是否為雙精度浮點值之間。Beyond this range, integers in JavaScript are not safe anymore and will be a double-precision floating point approximation of the value.

The number type has only one integer that has two representations: 0 is represented as -0 and +0. ("0" is an alias for +0). In the praxis, this has almost no impact. For example `+0 === -0` is `true`. However, you are able to notice this when you divide by zero:

```js
> 42 / +0
Infinity
> 42 / -0
-Infinity
```

Although a number often represents only its value, JavaScript provides some binary operators (en-US). These can be used to represent several Boolean values within a single number using bit masking. However, this is usually considered a bad practice, since JavaScript offers other means to represent a set of Booleans (like an array of Booleans or an object with Boolean values assigned to named properties). Bit masking also tends to make code more difficult to read, understand, and maintain. It may be necessary to use such techniques in very constrained environments, like when trying to cope with the storage limitation of local storage or in extreme cases when each bit over the network counts. This technique should only be considered when it is the last measure that can be taken to optimize size.

### 字串型別

JavaScript 的 `字串`型別用來代表文字資料。它是一組 16 位的未宣告「元素」值。每個字串的元素，在字串內皆佔有一位。第一個元素位於索引的第 0 位，下一個元素位於第 1 位，並依此類推。字串的長度，是指該字串有多少元素。

與 C 這類的語言不同，JavaScript 字串是不變的，意思是說當字串被創造出來以後，你不可能修改它。不過，可以基於操作原來的字串，來產生新的字串。例如：

* 透過挑選單個字母或使用 `String.substr()` (en-US) 的原始子字串。
* 使用級聯運算符（`+`）或 `String.concat()` 連接兩個字符串。

#### Beware of "stringly-typing" your code!

It can be tempting to use strings to represent complex data. Doing this comes with short-term benefits:

* It is easy to build complex strings with concatenation.
* Strings are easy to debug (what you see printed is always what is in the string).
* Strings are the common denominator of a lot of APIs (input fields (en-US), local storage values, `XMLHttpRequest` responses when using `responseText`, etc.) and it can be tempting to only work with strings.

With conventions, it is possible to represent any data structure in a string. This does not make it a good idea. For instance, with a separator, one could emulate a list (while a JavaScript array would be more suitable). Unfortunately, when the separator is used in one of the "list" elements, then, the list is broken. An escape character can be chosen, etc. All of this requires conventions and creates an unnecessary maintenance burden.

Use strings for textual data. When representing complex data, parse strings and use the appropriate abstraction.

### Symbol type

Symbols are new to JavaScript in ECMAScript Edition 6. A Symbol is a **unique** and **immutable** primitive value and may be used as the key of an Object property (see below). In some programming languages, Symbols are called atoms. You can also compare them to named enumerations (enum) in C. For more details see Symbol (en-US) and the `Symbol` (en-US) object wrapper in JavaScript.

Objects
-------

以資訊科學而言，物件是個能透過identifier參照的有數值記憶體。

### Properties

In JavaScript, objects can be seen as a collection of properties. With the object literal syntax, a limited set of properties are initialized; then properties can be added and removed. Property values can be values of any type, including other objects, which enables building complex data structures. Properties are identified using key values. A key value is either a String or a Symbol value.

There are two types of object properties which have certain attributes: The data property and the accessor property.

#### Data property

Associates a key with a value and has the following attributes:

| Attribute | Type | Description | Default value |
| --- | --- | --- | --- |
| [[Value]] | Any JavaScript type | The value retrieved by a get access of the property. | undefined |
| [[Writable]] | Boolean | If `false`, the property's [[Value]] can't be changed. | false |
| [[Enumerable]] | Boolean | If `true`, the property will be enumerated in for...in loops. See also Enumerability and ownership of properties (en-US) | false |
| [[Configurable]] | Boolean | If `false`, the property can't be deleted and attributes other than [[Value]] and [[Writable]] can't be changed. | false |

| Attribute | Type | Description |
| --- | --- | --- |
| Read-only | Boolean | Reversed state of the ES5 [[Writable]] attribute. |
| DontEnum | Boolean | Reversed state of the ES5 [[Enumerable]] attribute. |
| DontDelete | Boolean | Reversed state of the ES5 [[Configurable]] attribute. |

#### Accessor property

Associates a key with one or two accessor functions (get and set) to retrieve or store a value and has the following attributes:

| Attribute | Type | Description | Default value |
| --- | --- | --- | --- |
| [[Get]] | Function object or undefined | The function is called with an empty argument list and retrieves the property value whenever a get access to the value is performed. See also `get` (en-US). | undefined |
| [[Set]] | Function object or undefined | The function is called with an argument that contains the assigned value and is executed whenever a specified property is attempted to be changed. See also `set` (en-US). | undefined |
| [[Enumerable]] | Boolean | If `true`, the property will be enumerated in for...in loops. | false |
| [[Configurable]] | Boolean | If `false`, the property can't be deleted and can't be changed to a data property. | false |

**備註：** Attribute is usually used by JavaScript engine, so you can't directly access it(see more about Object.defineProperty()).That's why the attribute is put in double square brackets instead of single.

### "Normal" objects, and functions

A JavaScript object is a mapping between keys and values. Keys are strings (or `Symbol` (en-US)s) and values can be anything. This makes objects a natural fit for hashmaps.

Functions are regular objects with the additional capability of being callable.

### Dates

When representing dates, the best choice is to use the built-in `Date` utility in JavaScript.

### Indexed collections: Arrays and typed Arrays

Arrays are regular objects for which there is a particular relationship between integer-keyed properties and the `length` property.

Additionally, arrays inherit from `Array.prototype`, which provides a handful of convenient methods to manipulate arrays. For example, `indexOf()` searches a value in the array and `push()` appends an element to the array. This makes Arrays a perfect candidate to represent ordered lists.

Typed Arrays present an array-like view of an underlying binary data buffer, and offer many methods that have similar semantics to the array counterparts. "Typed array" is an umbrella term for a range of data structures, including `Int8Array`, `Float32Array`, etc. Check the typed array page for more information. Typed arrays are often used in conjunction with `ArrayBuffer` and `DataView` (en-US).

### Keyed collections: Maps, Sets, WeakMaps, WeakSets

These data structures take object references as keys and are introduced in ECMAScript Edition 6. `Set` and `WeakSet` (en-US) represent a set of objects, while `Map` and `WeakMap` (en-US) associate a value to an object. The difference between Maps and WeakMaps is that in the former, object keys can be enumerated over. This allows garbage collection optimizations in the latter case.

One could implement Maps and Sets in pure ECMAScript 5. However, since objects cannot be compared (in the sense of "less than" for instance), look-up performance would necessarily be linear. Native implementations of them (including WeakMaps) can have look-up performance that is approximately logarithmic to constant time.

Usually, to bind data to a DOM node, one could set properties directly on the object or use `data-*` attributes. This has the downside that the data is available to any script running in the same context. Maps and WeakMaps make it easy to privately bind data to an object.

### Structured data: JSON

JSON (JavaScript Object Notation) is a lightweight data-interchange format, derived from JavaScript but used by many programming languages. JSON builds universal data structures. See JSON and `JSON` for more details.

### More objects in the standard library

JavaScript has a standard library of built-in objects. Please have a look at the reference to find out about more objects.

使用 `typeof` 運算子來判斷型別
--------------------

`typeof` 運算子可以幫助你找到你的變數型別，請閱讀〈reference page 〉來取得更多細節及邊緣案例。

See also
--------

* Nicholas Zakas collection of common data structure and common algorithms in JavaScript.
* Search Tre(i)es implemented in JavaScript


重新介紹 JavaScript - JavaScript

重新介紹 JavaScript
===============

介紹
--

為何需要重新介紹？因為 JavaScript 堪稱是全世界最被人誤解的程式語言。儘管 JavaScript 再怎麼的被嘲諷為小兒科，在它誤導人的簡潔下隱藏著強大的語言功能。2005 年是個許多知名 JavaScript 應用程式推出的年度，在在證明：更加瞭解這項科技對任何網頁開發者來說皆是重要的技能。

先從該語言的歷史說起。1995 年，Brendan Eich，一位 Netscape （網景）的工程師，創造了 JavaScript。1996 年初，JavaScript 隨著 Netscape 2 首次推出。它原本要被命名為 LiveScript，結果因為行銷策略為了強調昇陽的 Java 程式語言的普遍性，而不幸的被改名 — 即便兩者之間沒有太大的關係。從此之後，這便成為了混淆的元兇。

Microsoft 在幾個月後隨著 IE 3 推出了跟該語言大致上相容的 JScript。Netscape 在 1997 年將該語言送交 ECMA International，一個歐洲標準化組織，而在 1997 年的時候產生了初版的 ECMAScript。該標準在 1999 年的時候以 ECMAScript 第三版的形式推出了更新，從此之後大致上都相當穩定，不過近期有在研發第四版。

這個穩定性對開發者來說是相當好的事情，因為它讓不少實作 (implementation) 有時間慢慢趕上。我會把重點放在第三版的語法。為了避免混淆，我會繼續使用 JavaScript 這個名稱。

與其他程式語言大大不同的是，JavaScript 沒有任何輸入或輸出的觀念。它是被設計成在一個宿主 (host) 環境下執行的腳本 (script) 語言，所以任何與外界通訊的方式，都是宿主環境的責任。瀏覽器是最常見的宿主環境，不過有些程式也有 JavaScript 解釋器，如 Adobe Acrobat、Photoshop、以及 Yahoo! Widget Engine 等等。

### 概要

先從任何語言最基本的方面講起：型態 (type)。JavaScript 程式可以改變「值」(value)，而這些值各自有其歸屬的型態。JavaScript 的型態有：

* Number （數字）
* String （字串）
* Boolean （布林值）
* Function （函式）
* Object （物件）

...以及稍微怪一點的 undefined （未定義）和 null （空）。還有，算是一種特殊物件的 Array （陣列）。還有，額外的特殊物件 Date （日期）以及 Regular Expression。另外，如果真的要達到技術上的準確性，連函式也只算是一種特殊的物件。所以型態分佈表看起來應該像這樣：

* Number （數字）
* String （字串）
* Boolean （布林）
* Object （物件）
    + Function （函式）
    + Array （陣列）
    + Date （日期）
    + RegExp
* Null （空）
* Undefined （未定義）

其實也有內建的 Error （錯誤）類型，不過，先把重點放在上面的分佈表比較容易。

### 數字

根據規格，JavaScript 數字算是「雙精確度 64 位元格式 IEEE 754 值」("double-precision 64-bit format IEEE 754 values")。這能造成一些有趣的後果。JavaScript 沒有所謂的整數，所以你在做算術的時候得小心一點，尤其是假如你習慣了 C 或 Java 的數學。小心類似下的的事情：

```js
0.1 + 0.2 = 0.30000000000000004
```

JavaScript 支援標準的數字運算子，包含加法、減法、取餘數、等算術。另外還有一個之前忘記提的內建物件，Math （數學），用以處理較為進階的數學函數和常數：

```js
Math.sin(3.5);
d = Math.PI \* r \* r;
```

你可以用內建的 `parseInt()` 函式將字串轉成整數。這個函式有個可選用的第二個參數（在此建議你一定要指定），用以指定進位數。

```js
> parseInt("123", 10)
123
> parseInt("010", 10)
10
```

如果你不指定進位數，就有可能得到意想不到的結果：

```js
> parseInt("010")
8
```

這是因為 `parseInt` 函數把字串當成八進位的數字，因為開頭有個 0。

如果要把二進位的數字轉成整數，只要把進位數改掉就行了：

```js
> parseInt("11", 2)
3
```

有個特殊的數字，叫做 `NaN` （「Not a Number」，「非數字」的簡稱），假如遞進去的字串不是數字，則會回傳 `NaN` ：

```js
> parseInt("hello", 10)
NaN
```

`NaN` 很毒：將其進行任何數學運算，結果仍會是 `NaN`：

```js
> NaN + 5
NaN
```

你可以用內建的 `isNaN()` 函式來判斷一個值是否為 `NaN`：

```js
> isNaN(NaN)
true
```

JavaScript 也有特殊的值 `Infinity` （無限）以及 `-Infinity` （負無限）：

```js
> 1 / 0
Infinity
> -1 / 0
-Infinity
```

### 字串

JavaScript 的字串是一序列的字元。更精確的說，是一序列的 Unicode 字元，每個字元皆以一個 16 位元的數字作為代表。這讓任何人不需要為國際化感到擔心。

如果你要代表一個單一字元，用長度為 1 的字串即可。

要得知一個字串的長度，請存取該字串的 `length`（長度）屬性：

```js
> "hello".length
5
```

剛剛可是與 JavaScript 物件的第一次接觸呢！字串也是物件喔。字串甚至也有方法 (method)：

```js
> "hello".charAt(0) //位置 0 的字元
h
> "hello, world".replace("hello", "goodbye") //把 hello 換成 goodbye
goodbye, world
> "hello".toUpperCase() //轉成大寫
HELLO
```

### 其他類型

JavaScript 對下列兩者是有分別的：`null` （空），屬於「object」型態的一種物件，用以明言表示無數值，以及 `undefined` （無定義），屬於「undefined」類型的一種物件，用以表示未初始化的值，也就是說，根本還沒指定數值。雖然姑且先不論變數，但在 JavaScript 你可以宣告一個變數但不指定其值。如果你這麼做的話，那該變數的型態便是 `undefined`。

JavaScript 有布林 (boolean) 型態，可能的值有 `true` （真）與 `false` （假）且兩者皆為關鍵字。根據下列規則，任何值都可以被轉換成布林值：

1. `false`、`0`、空字串 (`""`)、`NaN`、`null`、以及 `undefined` 都會成為 `false`
2. 所有其他的值都會成為 `true`

你可以用 `Boolean()` 函式特別進行轉換：

```js
> Boolean("")
false
> Boolean(234)
true
```

不過很少需要這樣，因為假如 JavaScript 遇到需要接收布林值的時候，如 `if` 陳述式（見下），便會無聲無息的進行布林轉換。有鑑於此，常常會有「真值」("true values") 與「假值」("false values") 等說法，意思是指值在布林轉換過程中會被轉成 `true` 或是 `false`。這種值也有被稱為「真的」("truthy") 或「假的」("falsy")。

JavaScript 支援布林運算，如 `&&` （邏輯「*與*」，英稱 *and*）、`||` （邏輯「*或*」，英稱 *or*）、以及 `!` （邏輯「*非*」，英稱 *not*），請見下。

### 變數

在 JavaScript，要宣告新變數，使用的是 `var` 關鍵字：

```js
var a;
var name = "simon";
```

如果你宣告一個變數但不指定任何值，其型態便為 `undefined` （未定義）。 應該注意關於 JS 的區塊不會構成新變數作用域

### 運算子

JavaScript 的數字運算子有 `+`、`-`、`*`、`/`、以及 `%` - 最後一個是取餘數的運算子（英稱 *mod*）。用來指定值的運算子是 `=`，另外還有複合指定陳述式，如 `+=` 以及 `-=`。這些是用以延伸 `x = x 運算子 y`。

```js
x += 5;
x = x + 5;
```

你可以用 `++` 和 `--` 來分別增加或是減少數值。這些運算子可以放在變數的開頭或結尾。

`+` 運算子也能把字串連接 (concatenate) 起來:

```js
> "hello" + " world"
hello world
```

如果你把一字串加到一個數字（或其他數值），會先把所有的東西轉成字串。這會讓你意想不到：

```js
> "3" + 4 + 5
345
> 3 + 4 + "5"
75
```

把一個空字串加到一個東西是個將其轉成字串的好方法之一。

JavaScript 中進行比較可以用 `<`、`>`、`<=`、以及 `>=`。這些對字串和數字都有用。等值比較 (equality) 比較沒那麼直接。雙等號運算子（等於）會進行型態強制轉換，假如比較的資料型態不一樣，有時結果會相當有趣：

```js
> "dog" == "dog"
true
> 1 == true
true
```

要避免型態強制轉換，要用三等號運算子（絕對等於）：

```js
> 1 === true
false
> true === true
true
```

另外還有 `!=` （不等於）以及 `!==` （絕對不等於）運算子。

假如你需要用的話，JavaScript 也有逐位元 (bitwise) 運算子。

### 控制結構

JavaScript 跟其他同屬 C 家族的程式語言有類似的控制結構。條件陳述式是靠 `if` 以及 `else` 來表示。如果你喜歡還可以串起來：

```js
var name = "貓咪";
if (name == "狗狗") {
  name += "!";
} else if (name == "貓咪") {
  name += "!!";
} else {
  name = "!" + name;
}
name == "貓咪!!";
```

JavaScript 有 `while` 迴圈以及 `do-while` 迴圈。前者適合做基本的迴圈，而後者是當你要迴圈至少執行一次就需要用到：

```js
while (true) {
  // 無限迴圈！
}

do {
  var input = get\_input();
} while (inputIsNotValid(input));
```

JavaScript 的 `for` 迴圈跟 C 和 Java 的一樣：可以讓你用一行就提供控制的條件與資訊。

```js
for (var i = 0; i < 5; i++) {
  // 會執行 5 次
}
```

`&&` 以及 `||` 運算子用的是「短路邏輯」(short-circuit logic)，也就是說，第二個運算值是否會被執行靠的是第一個運算值。這用來在存取一個物件的屬性前檢查物件是否為空 (null) 非常有用：

```js
var name = o && o.getName();
```

或是用來設預設值：

```js
var name = otherName || "預設";
```

JavaScript 也有三元運算子 (tertiary operator)，可以用來寫單行的條件陳述式：

```js
var allowed = age > 18 ? "是" : "否";
```

switch 陳述式可以根據一個數字或字串做不同決定：

```js
switch (action) {
  case "畫":
    drawit(); //開始畫
    break; //中斷
  case "吃":
    eatit(); //開始吃
    break; //中斷
  default: //預設
    donothing(); //不做任何事
}
```

如果你不加個 `break` （中斷）陳述式，執行的程式碼會往下「掉」一層。你很少會需要這樣 - 不過假如你真的要這樣，最好用個註解說明一下，這樣才方便除錯：

```js
switch (a) {
  case 1: //往下「掉」一層
  case 2:
    eatit(); //開始吃
    break; //中斷
  default: //預設
    donothing(); //不做任何事
}
```

default 子句 (clause) 是選擇性的，可有可無。如果你喜歡的話，你可以在 switch 部分或 case 部分放表達式 (expression)；兩者之間的比較使用的會是 `===` 運算子：

```js
switch (1 + 3) {
  case 2 + 2:
    yay(); //耶！
    break; //中斷
  default: //預設
    neverhappens(); //根本不會發生
}
```

### 物件

JavaScript 物件是一系列的「名稱對數值組合」(name-value pair)。有鑑於此，它們和下列的東西很相近:

* Python 的 dictionary （字典）
* Perl 和 Ruby 的 hash （雜湊）
* C 和 C++ 的 hash table （雜湊表）
* Java 的 HashMap （雜湊地圖）
* PHP 的 associative array （聯合陣列）

這種資料結構的普遍性證明了其多樣性。由於 JavaScript 的任何東西（核心類型以外）都是物件，任何 JavaScript 程式都自然而然的用到許多雜湊表查詢。還好這些查詢的速度都很快！

「名稱」的部分是個 JavaScript 字串，而「數值」可以是任何 JavaScript 值－－包括物件。這可以讓你隨心所欲的建構複雜的資料結構。

建立空物件有種基本方法：

```js
var obj = new Object();
```

以及：

```js
var obj = {};
```

這兩者在意義上相等；後者叫做物件實體語法 (object literal)，比較方便。早期並沒有物件實體語法，也就是為何許多程式碼用的還是舊的方法。

一旦建立了，一個物件的屬性可以用兩種方法存取：

```js
obj.name = "小明";
var name = obj.name;
```

還有...

```js
obj["name"] = "小明";
var name = obj["name"];
```

這兩者也是在意義上相等。第二種方法的優點是屬性的名稱可以在執行的時候以字串提供，也就是說可以動態的變動。這也可以用來取得和設定名稱是保留關鍵字 (reserved keyword) 的屬性：

```js
obj.for = "Simon"; //語法錯誤
obj["for"] = "Simon"; //沒問題
```

物件實體語法可以一次把物件完全初始化：

```js
var obj = {
  name: "胡蘿蔔", //名稱
  for: "小華", //給誰
  details: {
    //詳細資訊
    color: "橘", //顏色
    size: 12, //大小
  },
};
```

存取屬性也可以連在一起：

```js
> obj.details.color
橘
> obj["details"]["size"]
12
```

### 陣列

JavaScript 的陣列其實是一種特殊的物件。它們的運作方式跟正常的物件很像（數字性的屬性只能透過 [] 語法進行存取），不過有個神奇的屬性，叫做「length」（長度）。這個屬性一定是陣列最高索引數加一。

建立陣列的舊方法如下：

```js
> var a = new Array();
> a[0] = "狗";
> a[1] = "貓";
> a[2] = "雞";
> a.length
3
```

比較方便的語法便是使用陣列實體語法：

```js
> var a = ["狗", "貓", "雞"];
> a.length
3
```

在陣列實體語法結尾留個空逗點在各瀏覽器間結果參差不齊，所以最好不要這樣：

```js
> var a = ["狗", "貓", "雞", ]; //最好不要這麼做
```

注意－－`array.length` 不一定是陣列的項目數。比方說：

```js
> var a = ["狗", "貓", "雞"];
> a[100] = "狐";
> a.length
101
```

別忘了－－陣列的 length 就是最高索引數加一。

如果你查詢一個不存在的陣列索引，得到的就是 `undefined`：

```js
> typeof(a[90])
undefined
```

利用上述，便可以像下列一樣在陣列上做迴圈：

```js
for (var i = 0; i < a.length; i++) {
  //處理 a[i]
}
```

這樣不是很有效率，因為每迴圈一次就會查詢一次 length 屬性。比較好的做法是：

```js
for (var i = 0, len = a.length; i < len; i++) {
  //處理 a[i]
}
```

一個更棒的寫法是：

```js
for (var i = 0, item; (item = a[i]); i++) {
  //處理 item
}
```

這裡設定了兩個變數。`for` 迴圈中間指定變數值的部分會被測試是否為「真的」(truthy)－－如果成功了，迴圈便會繼續。由於 `i` 每次都會加一，陣列內的每個項目會被照順序指定到變數 item。當偵測到「假的」(falsy) 項目時（如 `undefined`）迴圈便會停止。

注意－－這個小技巧只該用在你確定不會含有「假的」值的陣列（比如說一陣列的物件或 DOM 節點）。假如你在可能含有 0 的數字資料或可能含有空字串的字串資料上做迴圈，最好還是用 `i, j` 的方式。

另外一個做迴圈的方法是用 `for...in` 迴圈。不過，假如有人用 `Array.prototype` 新增新的屬性，那些屬性也會被這種迴圈讀到：

```js
for (var i in a) {
  //處理 a[i]
}
```

假如你要在陣列結尾加入項目，最安全的方法是這樣：

```js
a[a.length] = item; //同 a.push(item);
```

由於 `a.length` 一定是最高索引數加一，你可以很確定你指定到的是陣列結尾的空間。

陣列附有一些方法 (method)：

```js
a.toString(), a.toLocaleString(), a.concat(item, ..), a.join(sep),
a.pop(), a.push(item, ..), a.reverse(), a.shift(), a.slice(start, end),
a.sort(cmpfn), a.splice(start, delcount, [item]..), a.unshift([item]..)
```

* `concat` 結合，會傳回加入了新項目的新陣列
* `pop` 會移除最後一個項目並將其傳回
* `push` 會在結尾加入一或多個項目（就像前面提的 `ar.length` 方法）
* `slice` 傳回副陣列
* `sort` 進行排序，可選擇性的接受「比較性函數」(comparison function)
* `splice` 讓你透過刪除一個區塊並以更多項目代替來修改陣列
* `unshift` 會在開頭加入一或多個項目
### 函式

如同物件，函式 (function) 是瞭解 JavaScript 的核心元件。最基本的函式再簡單不過了：

```js
function add(x, y) {
  var total = x + y;
  return total;
}
```

這示範了基本函式的一切。一個 JavaScript 函式可以接受零或多個有名 (named) 參數。函式內文 (body) 要有多少陳述式就有多少陳述式，且可以宣告對於函式而言本地 (local) 的變數。`return` 陳述式可以在任何時候傳回一值並終止函式。如果沒有 return 陳述式（或者光是 return，沒有值），JavaScript 便會傳回 `undefined`。

有名參數比較像是做為參考，而非強制性的。你可以呼叫一個函式但不提供其要求的參數，引此傳入的便是 `undefined`。

```js
> add()
NaN // undefined 不能進行加法
```

你也可以傳入超過函式要求的參數數目：

```js
> add(2, 3, 4)
5 // 加了前兩數，不理 4
```

這或許有些可笑，但函式在內文內還可以存取一個叫做 `arguments` 的變數，一個類似陣列的物件，內含所有遞給函式的值。改寫一下 add 函式便可以使其接受無限量的值：

```js
function add() {
    var sum = 0;
    for (var i = 0, j = arguments.length; i < j; i++) {
        sum += arguments[i];
    }
    return sum;
}

> add(2, 3, 4, 5)
14
```

這樣並沒有比直接寫 `2 + 3 + 4 + 5` 來得有用。寫個算平均的函式吧：

```js
function avg() {
    var sum = 0;
    for (var i = 0, j = arguments.length; i < j; i++) {
        sum += arguments[i];
    }
    return sum / arguments.length;
}
> avg(2, 3, 4, 5)
3.5
```

這樣滿有用的，不過又有新問題了。`avg()` 函式接受的是個逗號分隔的參數清單－－不過如果你要取一陣列的平均值呢？你可以把函式重寫成這樣：

```js
function avgArray(arr) {
    var sum = 0;
    for (var i = 0, j = arr.length; i < j; i++) {
        sum += arr[i];
    }
    return sum / arr.length;
}
> avgArray([2, 3, 4, 5])
3.5
```

但是最好是可以重複利用已經建立好的函式。幸運的是，JavaScript 可以讓你以一陣列的參數來呼叫一個函式。這靠的是使用任何函式物件的 `apply()` 方法。

```js
> avg.apply(null, [2, 3, 4, 5])
3.5
```

`apply()` 的第二個參數便是做為一系列參數的陣列；第一個參數稍後才會討論。重點是，函式也是物件。

JavaScript 可以讓你建立匿名 (anonymous) 函式。

```js
var avg = function () {
  var sum = 0;
  for (var i = 0, j = arguments.length; i < j; i++) {
    sum += arguments[i];
  }
  return sum / arguments.length;
};
```

這個語法上和 `function avg()` 形式相等。這是非常強大的功能，因為你可以藉此在平常該放表達式的地方塞入一個完整的函式定義。這可以讓你用在各種令人拍案叫絕的技巧上。下列可以把本地 (local) 參數「藏」起來－－像 C 的 block scope 一樣：

```js
> var a = 1;
> var b = 2;
> (function() {
    var b = 3;
    a += b;
})();
> a
4
> b
2
```

JavaScript 能讓你遞迴地呼叫函式。這在處理樹狀結構的時候特別有用，比如瀏覽器的 DOM。

```js
function countChars(elm) {
  if (elm.nodeType == 3) {
    // TEXT\_NODE
    return elm.nodeValue.length;
  }
  var count = 0;
  for (var i = 0, child; (child = elm.childNodes[i]); i++) {
    count += countChars(child);
  }
  return count;
}
```

以上揭露了一個使用匿名函式的潛在問題：如果匿名函式沒有名稱，那要怎麼樣遞迴地自我呼叫？答案是使用 `arguments` 物件。該物件除了提供一系列的參數以外，還提供了一個叫做 `arguments.callee` 的屬性。這個屬性所指向的是目前的函式，因此可以用來做遞迴的呼叫：

```js
var charsInBody = (function (elm) {
  if (elm.nodeType == 3) {
    // TEXT\_NODE
    return elm.nodeValue.length;
  }
  var count = 0;
  for (var i = 0, child; (child = elm.childNodes[i]); i++) {
    count += arguments.callee(child);
  }
  return count;
})(document.body);
```

由於 `arguments.callee` 是目前的函式，而所有的函式都是物件，你可以因此用 `arguments.callee` 來在多次呼叫同個函式之間儲存資料。這個函式會記得它被呼叫過多少次：

```js
function counter() {
    if (!arguments.callee.count) {
        arguments.callee.count = 0;
    }
    return arguments.callee.count++;
}

> counter()
0
> counter()
1
> counter()
2
```

### 自訂物件

就典型的物件導向程式設計而言，物件是資料 (data) 以及運算該資料的方法 (method) 所構成的集合體 (collection)。我們以一個含有姓與名兩個欄位的「person」（人）物件來做為例子。在英文，一個人的姓名有兩種寫法：「名 姓」或「姓, 名」。利用之前探討的函式與物件，寫法如下：

```js
function makePerson(first, last) {
    return {
        first: first,
        last: last
    }
}
function personFullName(person) {
    return person.first + ' ' + person.last;
}
function personFullNameReversed(person) {
    return person.last + ', ' + person.first
}
> s = makePerson("Simon", "Willison");
> personFullName(s)
Simon Willison
> personFullNameReversed(s)
Willison, Simon
```

雖然這樣行得通，可是這樣很醜，在全域命名空間 (global namespace) 裡灑了一堆函式。我們需要把函式「附著」(attach) 到物件上。由於函式也是物件，這麼做並不難：

```js
function makePerson(first, last) {
    return {
        first: first,
        last: last,
        fullName: function() {
            return this.first + ' ' + this.last;
        },
        fullNameReversed: function() {
            return this.last + ', ' + this.first;
        }
    }
}
> s = makePerson("Simon", "Willison")
> s.fullName()
Simon Willison
> s.fullNameReversed()
Willison, Simon
```

這裡出現了之前沒有提過的 '`this`' 關鍵字。在一個函式內，「`this`」 指的是目前的物件。其真正的意義是經由你呼叫函數的方式來指定。如果你透過在物件上使用點或中括號記號來呼叫它，這物件就成為「`this`」。如果在呼叫中沒有使用點記號，「`this`」就會參考到全域物件. 這經常造成錯誤。舉例來說：

```js
> s = makePerson("Simon", "Willison")
> var fullName = s.fullName;
> fullName()
undefined undefined
```

當我們呼叫`fullName()`，「`this`」被繫結到全域物件。 既然沒有叫做 `first` 或 `last` 的全域變數，我們取得的都是 `undefined` 。

我們可以利用「`this`」關鍵字的好處來改進我們的 `makePerson` 函式：

```js
function Person(first, last) {
  this.first = first;
  this.last = last;
  this.fullName = function () {
    return this.first + " " + this.last;
  };
  this.fullNameReversed = function () {
    return this.last + ", " + this.first;
  };
}
var s = new Person("Simon", "Willison");
```

我們引入了另一個關鍵字：「`new`」。 「`new」`與「`this`」有強烈的關係。 他的作用是產生一個全新的空物件，然後呼叫指定的函式，使用新物件的作為函式的「`this`」。 被「new」呼叫的函式叫做「建構子函數」(constructor functions) 。

此時我們可以發現，每次產生一個新的 Person 物件都會在其內部重新產生兩個新的 function 物件。如果這兩個 function 物件只有一份而讓大家共用不是更好嗎？所以

```js
function personFullName() {
  return this.first + " " + this.last;
}
function personFullNameReversed() {
  return this.last + ", " + this.first;
}
function Person(first, last) {
  this.first = first;
  this.last = last;
  this.fullName = personFullName;
  this.fullNameReversed = personFullNameReversed;
}
```

好了，如此一來我們達到每一個 Person 物件都共用同一組 function 物件了。

那能不能做的更好呢？

```js
function Person(first, last) {
  this.first = first;
  this.last = last;
}
Person.prototype.fullName = function () {
  return this.first + " " + this.last;
};
Person.prototype.fullNameReversed = function () {
  return this.last + ", " + this.first;
};
```

Person.prototype 是一個由所有 Person 物件共享的物件。他將產生一個可供查看的關係鍊 (有個特殊的名字 prototype chain)。任何時候當我們想要使用某個不在 Person 中定義的 property 時，JavaScript 就會到 Person.prototype 裡頭尋找。因此， Person.prototype 就成為一個所有 Person 物件共用且可視的一個共享空間(物件)。這是一個提供強大工具，允許你可以在執行的任何一刻增加物件的相關函式。

```js
> s = new Person("Simon", "Willison");
> s.firstNameCaps();
TypeError on line 1: s.firstNameCaps is not a function
> Person.prototype.firstNameCaps = function() {
    return this.first.toUpperCase()
}
> s.firstNameCaps()
SIMON
```

另外，有趣的是，在 JavaScript 中，你不只能加入東西到你自創的物件，甚至可以加入到 JavaScript 原生的物件中！如下面舉例：

```js
> var s = "Simon";
> s.reversed()
TypeError on line 1: s.reversed is not a function
> String.prototype.reversed = function() {
    var r = "";
    for (var i = this.length - 1; i >= 0; i--) {
        r += this[i];
    }
    return r;
}
> s.reversed()
nomiS
```

我們新加入 String 的 reversed 函式亦可反應在字串物件上！如下所示：

```js
> "This can now be reversed".reversed()
desrever eb won nac sihT
```

承接以前提過的，prototype 會是 chain 的一部分，是以很直觀的，根就是 Object.prototype 了。在 Object.prototype 提供的眾多函式中包含一個 toString() - 他在當你創建一個字串物件時被呼叫。因此我們可以利用他來對我們的 Person 物件 debug：

```js
> var s = new Person("Simon", "Willison");
> s.toString()
[object Object]
> Person.prototype.toString = function() {
    return '<Person: ' + this.fullName() + '>';
}
> s
<Person: Simon Willison>
```

還記得前面提過的 apply() 嗎？當時我們在 avg.apply() 的第一個參數送進 null。現在我們回過頭來解釋 apply()。其實 apply() 的第一個參數會被當成 "this"。舉例來說，我們可以利用 apply() 實作一個 trivialNew() 使他的效果等同於平時使用的 new()：

```js
function trivialNew(constructor) {
  var o = {}; // Create an object
  constructor.apply(o, arguments);
  return o;
}
```

當我們這樣做時，並不會產生一個類似於 new() 的函式在 prototype 中。此外，apply() 有一個姊妹函式叫做 call，差異在於 call() 接受一個可被擴展的參數串列而非一個陣列。

```js
function lastNameCaps() {
  return this.last.toUpperCase();
}
var s = new Person("Simon", "Willison");
lastNameCaps.call(s);
// Is the same as:
s.lastNameCaps = lastNameCaps;
s.lastNameCaps();
```

### 巢狀函式

JavaScript 函式宣告可以放在其他函式內。我們之前有在 `makePerson()` 函式見過這個。巢狀函式的一樣重要的功能是：它們可以存取其母函式 (parent function) 的領域 (scope) 內的變數：

```js
function betterExampleNeeded() {
  var a = 1;
  function oneMoreThanA() {
    return a + 1;
  }
  return oneMoreThanA();
}
```

這對於寫更容易維護的程式碼來說很有用。假如某函式需要其他一兩個函式，而這一兩個函式在整個程式的其他部分都不需要用到，你便可以把這些所謂「工具函式」(utility function) 給巢狀性地包在需要它們的函式內，這個主要函式再從其他地方呼叫。這樣便能保持全域領域 (global scope) 的函式不會太多。不在全域領界內塞太多函式是件好事情。

這也能反制全域變數 (global variable) 的誘惑。在撰寫複雜的程式碼的時候，常常會有想利用全域變數來在多個函式之間傳遞數值的這種誘惑－－但這麼做，便會導致程式碼非常難以維護。巢狀函式可與其母函式一起共用變數，因此你可以用這個原理來在適當的時機將好幾個函式配在一起，而不用「汙染」全域命名空間 (global namespace)－－這可以稱做「本地變數」 (local variable)。使用此技巧時應當小心，不過，此技巧相當有用。

### 閉包

接著我們介紹一種十分強大卻也常使人困惑的機制：閉包 (closure)。在解釋之前，我們先看看下面這段程式，猜猜執行的結果是什麼。

```js
function makeAdder(a) {
    return function(b) {
        return a + b;
    }
}
x = makeAdder(5);
y = makeAdder(20);
x(6)
?
y(7)
?
```

答案揭曉，makeAdder 創造了一個 "adder" 函式，這個新的 "adder" 接受一個參數並將這個參數和 "adder" 被創造時的參數加在一起。

這裡發生的事情很類似先前提過的「內部函式」(inner function)：一個新的函式被創造在別的函式內部，並且可以接觸到外面函式的變數。但不同的地方在於，內部函式中一旦回到上層函式，其先前創造的內部函式中的本地變數就消滅了（因為作用域結束了）。但在閉包中，這些本地變數卻**依然存在**（有一種本地變數在離開作用域的那一刻被凍結的感覺！）－－否則我們上面例子中的 adder 就無法運作了。說到這裡，你應該知道結果了。x(6) 是 11 而 y(7) 是 27。

更進一步解釋，在 JavaScript 中，只要你開始執行一個函式，那麼就會出現一個「作用域」物件。作用域物件可以根據傳入的參數來做初始化的動作。這聽起來有點類似存放所有全域變數和全域函數的全域物件，但他們卻有幾點不同。首先，一個新的作用域物件是在任何該函式被執行時才產生，而全域物件直接的被使用，而必須進入該作用域才能操作。

再論 makeAdder。當它被呼叫時，一個新的作用域物件被產生，並且帶著一個 property a 在其中（這是由呼叫 makeAdder 者傳入的）。隨後 makeAdder 回傳一個新創的函式。正常情況下，JavaScript 的垃圾回收機制理應回收這個作用域物件，但在我們的例子中，回傳的函式卻保留了一個可以再次訪問此作用域物件的參照。因此，這個作用域物件便沒被真正回收－－必須等到未來沒有任何參照可以指向它，方能回收。

多個作用域物件會組成作用域鏈 (scope chain)，類似於 JavaScript 物件系統中的原型鏈。

簡單地做個結論，閉包是作用域物件和一個函式的組合，反映了其被創造之時的狀態。閉包允許你保留狀態，而這是很常用的功能。

### 記憶體流失

雖然閉包很方便，但卻容易在 IE 中造成記憶體流失。

JavaScript 是具有垃圾回收特性的語言，物件的生滅都是由執行環境（如瀏覽器）所控制。

1. 記憶體配置：創造物件時
2. 記憶體釋放：當沒有參照可以指向該物件時

當瀏覽器在執行的時候，需要維護來自 DOM 的大量物件。IE 使用自己的垃圾回收機制，而這個管理機制和 JavaScript 的不同－－這就造成了記憶體流失。

在我們這裡的例子中，記憶體流失源自於環狀的相互參照 (circular reference)－－ JavaScript 物件及原生物件之間的相互參照。以下面的程式為例：

```js
function leakMemory() {
  var el = document.getElementById("el");
  var o = { el: el };
  el.o = o;
}
```

此時便發生了記憶體流失。除非 IE 重新啟動，否則 el 和 o 使用的記憶體皆無法被釋放。這種例子容易因不小心而出現。

其實記憶體流失很難引起注意，除非

1. 程式需要長期執行
2. 因記憶體流失而消耗大量的記憶體
3. 迴圈中明顯地浪費記憶體

閉包容易產生記憶體流失。如下舉例 :

```js
function addHandler() {
  var el = document.getElementById("el");
  el.onclick = function () {
    this.style.backgroundColor = "red";
  };
}
```

在這段程式中，我們設定了一個 element，讓它可以在被點選時變紅。這裡形成了記憶體流失。你發現了嗎？因為指向 el 的參照不經意地被由匿名內部函式產生的閉包連結了。這就在 JavaScript 物件 (function) 和原生物件 (el) 之間產生了相互參照。

在現實生活中充滿了類似的案例，但我們其實可以很容易地處理。看一個簡單的示範：

```js
function addHandler() {
  var el = document.getElementById("el");
  el.onclick = function () {
    this.style.backgroundColor = "red";
  };
  el = null;
}
```

如此一來我們解除了相互參照。

此外，尚有一種巧妙的方式可以藉由增加一個新的閉包來消除相互參照：

```js
function addHandler() {
  var clickHandler = (function () {
    this.style.backgroundColor = "red";
  })(function () {
    var el = document.getElementById("el");
    el.onclick = clickHandler;
  })();
}
```

在此例子中，隱藏了來自 clickHandler 產生的內容，因而消除了相互參照。

避免閉包還有另外一種方法：利用 `window.onunload` 事件消除相互參照。許多事件函示庫都會自動這麼做。

原始文件資訊
------

* 作者：Simon Willison
* 最後更新日期：2006 年 3 月 7 日
* 著作權：© 2006 Simon Willison，Creative Commons: Attribute-Sharealike 2.0 （創用：姓名標示-相同方式分享 2.0）授權。
* 更多資訊：其他關於這份教學的資訊（以及原作者所用的投影片）請前往原作者的網誌：Etech weblog。


繼承與原型鏈 - JavaScript

繼承與原型鏈
======

JavaScript 是個沒有實做 `class` 關鍵字的動態語言，所以會對那些基於類別（class-based）語言（如 Java 或 C++）背景出身的開發者來說會有點困惑。（在 ES2015 有提供 `class` 關鍵字，但那只是個語法糖，JavaScript 仍然是基於原型（prototype-based）的語言）。

講到繼承，JavaScript 就只有一個建構子：物件。每個物件都有一個連著其他**原型**（prototype）的私有屬性（private property）物件。原型物件也有著自己的原型，於是原型物件就這樣鏈結，直到撞見 `null` 為止：`null` 在定義裡沒有原型、也是**原型鏈**（prototype chain）的最後一個鏈結。

幾乎所有 JavaScript 的物件，都是在原型鏈最頂端的 `Object` (en-US) 實例。

雖然這常被認為是 JavaScript 的一個缺陷，但原型繼承模型實際上，比傳統的 classic 模型更強大。舉例來說，使用原型繼承模型建構一個 classic 模型是相當容易的。

使用原型鍊繼承
-------

### 繼承屬性

JavaScript 物件是一「包」動態的屬性（也就是**它自己**的屬性）並擁有一個原型物件的鏈結，當物件試圖存取一個物件的屬性時，其不僅會尋找該物件，也會尋找該物件的原型、原型的原型……直到找到相符合的屬性，或是到達原型鏈的尾端。

**備註：** 遵照 ECMAScript 標準的 `someObject.[[Prototype]]` 標記，用於指派 `someObject` 的原型。從 ECMAScript 2015 開始， `[[Prototype]]` 使用 `Object.getPrototypeOf()` 與 `Object.setPrototypeOf()` (en-US) 這兩個訪問器（accessors）訪問，等同於非標準，但各大瀏覽器已實做的 `__proto__` 屬性。

不要把 `someObject.[[Prototype]]` 與函式屬性 `func.prototype` 混淆了。它在函式被用作建構子的時候，指定 `[[Prototype]]` 要分派到所有由給定函式建立的物件實例（instance）。**`Object.prototype`** 屬性代表了原型屬性 `Object` (en-US)。

以下是嘗試存取屬性時會發生的事：

```js
// 利用含有 a 與 b 屬性的 f 函式，建立一個 o 物件：
let f = function () {
  this.a = 1;
  this.b = 2;
};
let o = new f(); // {a: 1, b: 2}

// 接著針對 f 函式的原型添加屬性
f.prototype.b = 3;
f.prototype.c = 4;

// 不要寫 f.prototype = {b:3,c:4}; 因為它會破壞原型鏈
// o.[[Prototype]] 有 b 與 c 的屬性：{b: 3, c: 4}
// o.[[Prototype]].[[Prototype]] 是 Object.prototype.
// 最後 o.[[Prototype]].[[Prototype]].[[Prototype]] 成了 null
// 這是原型鏈的結末，因為 null 按照定義並沒有 [[Prototype]]。
// 因此，整個原型鏈看起來就像：
// {a: 1, b: 2} ---> {b: 3, c: 4} ---> Object.prototype ---> null

console.log(o.a); // 1
// o 有屬性「a」嗎？有，該數值為 1。

console.log(o.b); // 2
// o 有屬性「b」嗎？有，該數值為 2。
// o 還有個原型屬性「b」，但這裡沒有被訪問到。
// 這稱作「property shadowing」。

console.log(o.c); // 4
// o 有屬性「c」嗎？沒有，那就找 o 的原型看看。
// o 在「o.[[Prototype]]」有屬性「c」嗎？有，該數值為 4。

console.log(o.d); // undefined
// o 有屬性「d」嗎？沒有，那就找 o 的原型看看。
// o 在「o.[[Prototype]]」有屬性「d」嗎？沒有，那就找 o.[[Prototype]] 的原型看看。
// o.[[Prototype]].[[Prototype]] 是 Object.prototype，預設並沒有屬性「d」，那再找他的原型看看。
// o 在「o.[[Prototype]].[[Prototype]].[[Prototype]]」是 null，停止搜尋。
// 找不到任何屬性，回傳 undefined。
```

給物件設定屬性，會令其建立自有的屬性。這個行為規則的唯一例外，就是碰上以 getter 或 setter (en-US) 繼承的屬性時。

### 繼承方法

Javascript 並沒有其他基於類別語言那般定義的方法。在 Javascript 裡，任何函式都能以屬性的方式加到物件中。一個被繼承的函式的行為就像是其他屬性一樣，其中也包含了上述的 property shadowing（在這種情況下叫做 method overriding）。

當繼承函式執行時，`this` 值指向繼承的物件，而不是在函式內擁有屬性的原型物件。

```js
var o = {
  a: 2,
  m: function () {
    return this.a + 1;
  },
};

console.log(o.m()); // 3
// 在這裡呼叫 o.m 時「this」指的是 o

var p = Object.create(o);
// p 是個從 o 繼承的物件

p.a = 4; // 在 p 建立屬性「a」
console.log(p.m()); // 5
// 呼叫 p.m 時「this」指的是 p
// 因此在 p 繼承 o 的函式 m 時，
// 「this.a」指的是 p.a：也就是 p 的自有屬性「a」
```

產生物件和原型鏈的幾種方法
-------------

### 含有語法結構的物件

```js
var o = { a: 1 };

// 新建的 o 有個自己的 [[Prototype]] 稱為 Object.prototype
// o 自己並沒有稱為「hasOwnProperty」的屬性
// 而 hasOwnProperty 是 Object.prototype 的自有屬性。
// 因此 o 從 Object.prototype 繼承了 hasOwnProperty
// Object.prototype 作為其原型多了個 null
// o ---> Object.prototype ---> null

var a = ["yo", "whadup", "?"];

// 從 Array.prototype 繼承的陣列，含有諸如 indexOf、forEach……之類的方法
// 原型鏈看起來就像：
// a ---> Array.prototype ---> Object.prototype ---> null

function f() {
  return 2;
}

// 從 Function.prototype 繼承的函式，含有諸如 call、bind……之類的方法
// f ---> Function.prototype ---> Object.prototype ---> null
```

### 透過建構子

JavaScript 建構子，就、只、是、個、被 new 運算子呼叫的函式。

```js
function Graph() {
  this.vertices = [];
  this.edges = [];
}

Graph.prototype = {
  addVertex: function (v) {
    this.vertices.push(v);
  },
};

var g = new Graph();
// g 是個有著「vertices」與「edges」屬性的物件。
// 在執行 new Graph() 時 g.[[Prototype]] 是 Graph.prototype 的值。
```

### 用 `Object.create`

ECMAScript 5 引入了新方法：`Object.create()`。呼叫這個方法就可以建立新的物件。這個物件的原型，為函式的第一個參數。

```js
var a = { a: 1 };
// a ---> Object.prototype ---> null

var b = Object.create(a);
// b ---> a ---> Object.prototype ---> null
console.log(b.a); // 1 (inherited)

var c = Object.create(b);
// c ---> b ---> a ---> Object.prototype ---> null

var d = Object.create(null);
// d ---> null
console.log(d.hasOwnProperty);
// undefined, because d doesn't inherit from Object.prototype
```

### 使用關鍵字 `class`

ECMAScript 2015 引入了新的類別實做。儘管對那些基於類別的開發者來說，這種結構體令他們感到熟悉，它們依舊不一樣。JavaScript 依舊是基於原型的。新的關鍵字包括 `class` (en-US)、`constructor`、`static`、`extends`、`super`。

```js
"use strict";

class Polygon {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}

class Square extends Polygon {
  constructor(sideLength) {
    super(sideLength, sideLength);
  }
  get area() {
    return this.height \* this.width;
  }
  set sideLength(newLength) {
    this.height = newLength;
    this.width = newLength;
  }
}

var square = new Square(2);
```

### 效能

原型鏈上的屬性的查詢時間，可能會對效能有負面影響，對程式碼也因而產生明顯問題。另外，試圖尋找不存在的屬性，就一定會遍歷整個原型鏈。

接著，在迭代物件屬性時，**每個**原型鏈的枚舉屬性都會抓出來。

要檢查物件*本身*有沒有指定的屬性、也不需要查找整個原型鏈時，你必須使用由 `Object.prototype` 繼承的 `hasOwnProperty` 方法。

```js
console.log(g.hasOwnProperty("vertices"));
// true

console.log(g.hasOwnProperty("nope"));
// false

console.log(g.hasOwnProperty("addVertex"));
// false

console.log(g.__proto__.hasOwnProperty("addVertex"));
// true
```

在 JavaScript 裡面 `hasOwnProperty` 是唯一能處理、且**不**遍歷整個原型鏈的方法。

註：如果只有檢查屬性是否為 `undefined` (en-US) **是不夠的**。該屬性可能存在，只是數值被設定為 `undefined`。

### 壞實做：擴充原生的原型

一個常見的誤用，是擴充 `Object.prototype` 或其他內建的原型。

這種技巧稱為猴子補丁（monkey patching），它會破壞*封裝*（encapsulation）。儘管有些受歡迎的框架如 Prototype.js 會這麼做，但它們並不是以額外的*非標準*功能，打亂內建類型的好理由。

擴充內建原型的**唯一**合理理由，就是反向移植新版 JavaScript 引擎的功能，例如 `Array.forEach`。

示例
--

`B` 要繼承自 `A`：

```js
function A(a) {
  this.varA = a;
}

// 在 A.prototype.varA 會永遠被 this.varA 鬼隱（shadowed）的情況下，
// 把 varA 包在給定函式的原型內，其意義為何？
A.prototype = {
  varA: null, // 難道不能因為原型啥都沒作，就把 varA 打斷嗎？
  // 也許他打算要分配隱藏類別的空間，以便實行最佳化？
  // https://developers.google.com/speed/articles/optimizing-javascript#Initializing instance variables
  // 如果 varA 沒有針對每個實例作單一初始化，那麼不就要變有效的？
  doSomething: function () {
    // ...
  },
};

function B(a, b) {
  A.call(this, a);
  this.varB = b;
}
B.prototype = Object.create(A.prototype, {
  varB: {
    value: null,
    enumerable: true,
    configurable: true,
    writable: true,
  },
  doSomething: {
    value: function () {
      // override
      A.prototype.doSomething.apply(this, arguments); // call super
      // ...
    },
    enumerable: true,
    configurable: true,
    writable: true,
  },
});
B.prototype.constructor = B;

var b = new B();
b.doSomething();
```

重點是：

* 型別被定義在 `.prototype`。
* 你用了 `Object.create()` 繼承。
`prototype` 與 `Object.getPrototypeOf`
-------------------------------------

JavaScript 對那些從 Java 或 C++ 學過來的人來說，可能會有點困惑，因為它動態、永遠是執行狀態（all runtime）、還完全沒有 class。一切都只是實例（物件）。即使是「class」關鍵字，也只是函式物件。

你可能早就發現我們的 `function A` 有個名為 `prototype` 的特殊屬性。這個特殊屬性會透過 `new` 運算子運行之。原型物件的參照，會把新實例的 `[[Prototype]]` 屬性複製到該物件內部。比方說在你 `var a1 = new A()` 時，JavaScript 在記憶體裡面建立物件後、還有運行已經定義好 `this` 的函式 `A()` 前，會設定 `a1.[[Prototype]] = A.prototype`。在接著訪問實例的屬性前，JavaScript 首先會直接檢查物件的實例屬性是否存在。如果沒有，就檢查 `[[Prototype]]`。也就是說，在 `prototype` 定義的東西，能夠有效地共享給各實，甚至，如果想的話，還可以改變並顯現於現有實例中。

如上例所示，首先 `var a1 = new A(); var a2 = new A();` 接著 `a1.doSomething` 的話，會參照到 `Object.getPrototypeOf(a1).doSomething`，他會與早就定義好的 `A.prototype.doSomething` 相同，例如： `Object.getPrototypeOf(a1).doSomething == Object.getPrototypeOf(a2).doSomething == A.prototype.doSomething`.

簡而言之，`prototype` 針對型別，而 `Object.getPrototypeOf()` 則和實例相同。

`[[Prototype]]` *遞歸性地*鏈結著，例如說 `a1.doSomething`, `Object.getPrototypeOf(a1).doSomething`, `Object.getPrototypeOf(Object.getPrototypeOf(a1)).doSomething` 這樣，直到找出 `Object.getPrototypeOf` 或回傳 null。

因此當你：

```js
var o = new Foo();
```

JavaScript 其實會：

```js
var o = new Object();
o.[[Prototype]] = Foo.prototype;
Foo.call(o);
```

或偶爾這樣：

```js
o.someProp;
```

時，它檢查了 `o` 有沒有 `someProp` 屬性。如果沒有，就檢查 `Object.getPrototypeOf(o).someProp`；再沒有就檢查 `Object.getPrototypeOf(Object.getPrototypeOf(o)).someProp`，依此類推。

結論
--

在撰寫複雜的可用程式碼之前，理解原型繼承模型**很重要**。另外，請注意程式碼內原型鏈的長度、必要時打破它們，以避免潛在的效能問題。再來，除非要處理 JavaScript 新語法的相容性，否則**絕對不能**擴充原生的原型。