Map - JavaScript

Map
===

**`Map`** 是保存了鍵值對（key-value pairs）的物件。任何值（包括物件及基本型別（primitive）值 (en-US)）都可以作為鍵或值。

語法
--

```js
new Map([iterable])
```

### 參數

`iterable`

為一個`陣列`或其他元素成鍵值對的可迭代 (en-US)物件（有兩個元素的陣列，例如 `[[ 1, 'one' ],[ 2, 'two' ]]`）。每一個鍵值對都會被加入至新的 `Map`；`null` 會被視為 `undefined`。

描述
--

一個 `Map` 物件會根據元素的新增順序走訪自身的所有元素 — `for...of` (en-US) 迴圈會在每次迭代回傳一個 `[key, value]` 陣列。

### 鍵的相等性

鍵相等是基於 SameValueZero 的演算法：`NaN` 被認為與 `NaN` 相同（即使 `NaN !== NaN`）並且根據 `===` 運算符的語義，所有其他值都被認為相等。在目前的 ECMAScript 規範中，`-0` 和 `+0` 被認為是相等的，儘管在早期的草案中並非如此。詳細的內容請參閱 瀏覽器相容性 表中的 "Value equality for -0 and 0"。

### Object 及 Map 的比較

`Object` (en-US) 和 `Map` 類似。兩者都允許你設置對應的鍵值對，檢索這些值，刪除鍵，並檢測是否有什麼存儲在鍵中。正因為如此（也因為沒有內置的替代品），`Object` 在歷史上一直被當作 `Map` 使用；然而在某些情況下，使用 `Map` 有一些重要的不同之處:

* `Object` 的鍵是 `字串` 和 `Symbol` (en-US)，而它們在 `Map` 中可以是任意的資料型態，包括函數，對象以及原始的資料型態。
* 你可以使用 `size` 屬性輕鬆地獲得 `Map` 的大小，而 `Object` 中的屬性數量必須手動確認。
* `Map` 是可迭代（iterable (en-US)）的，因此可以直接迭代，而在 `Object` 上迭代則需要以某種方式獲取其鍵並對其進行迭代。
* `Object` 有一個原型，所以如果不小心，映射中有一些默認鍵可能與鍵發生衝突。從 ES5 開始，這可以通過使用 `map = Object.create(null)` 來繞過這個問題，但是很少這樣做。
* 在涉及頻繁添加和刪除鍵值對的場景中，`Map` 可能表現得更好。
屬性
--

`Map.length`

`length`屬性的值為 0
 要計算 `Map` 中有多少元素，可以使用 `Map.prototype.size` (en-US)。

`get Map[@@species]` (en-US)

用於創建派生物件的構造函數。

`Map.prototype` (en-US)

表示 `Map` 構造函數的原型，允許對所有的 `Map` 物件添加屬性

`Map` 物件實體
----------

所有的 `Map` 實例都繼承自 `Map.prototype` (en-US).

### 屬性

`Map.prototype[@@toStringTag]`

The initial value of the `@@toStringTag` (en-US) property is the string `"Map"`. This property is used in `Object.prototype.toString()` (en-US).

`Map.prototype.size` (en-US)

Returns the number of key/value pairs in the `Map` object.

### 方法

`Map.prototype.clear()` (en-US)

Removes all key-value pairs from the `Map` object.

`Map.prototype.delete()` (en-US)

 Returns `true` if an element in the `Map` object existed and has been
 removed, or `false` if the element does not exist. `map.has(key)`
 will return `false` afterwards.

`Map.prototype.get()` (en-US)

Returns the value associated to the passed key, or `undefined` if there is none.

`Map.prototype.has()` (en-US)

Returns a boolean indicating whether a value has been associated with the passed key in the `Map` object or not.

`Map.prototype.set()` (en-US)

Sets the value for the passed key in the `Map` object. Returns the `Map` object.

`Map.prototype[@@iterator]()` (en-US)

Returns a new Iterator object that contains a two-member array of `[key, value]` for each element in the `Map` object in insertion order.

`Map.prototype.keys()` (en-US)

Returns a new Iterator object that contains the keys for each element in the `Map` object in insertion order.

`Map.prototype.values()` (en-US)

Returns a new Iterator object that contains the values for each element in the `Map` object in insertion order.

`Map.prototype.entries()` (en-US)

Returns a new Iterator object that contains a two-member array of `[key, value]` for each element in the `Map` object in insertion order.

`Map.prototype.forEach()` (en-US)

Calls `callbackFn` once for each key-value pair present in the `Map` object, in insertion order. If a `thisArg` parameter is provided to `forEach`, it will be used as the `this` value for each callback.

範例
--

### 使用 `Map` 物件

```js
var myMap = new Map();

var keyString = "a string",
  keyObj = {},
  keyFunc = function () {};

// setting the values
myMap.set(keyString, "value associated with 'a string'");
myMap.set(keyObj, "value associated with keyObj");
myMap.set(keyFunc, "value associated with keyFunc");

myMap.size; // 3

// getting the values
myMap.get(keyString); // "value associated with 'a string'"
myMap.get(keyObj); // "value associated with keyObj"
myMap.get(keyFunc); // "value associated with keyFunc"

myMap.get("a string"); // "value associated with 'a string'"
// because keyString === 'a string'
myMap.get({}); // undefined, because keyObj !== {}
myMap.get(function () {}); // undefined, because keyFunc !== function () {}
```

### 使用 `NaN` 作為 `Map` 的鍵

`NaN` 同樣可以作為 `Map` 的 key，雖然每個 `NaN` 都不等於自己本身，下面的例子是有效的，因為 `NaN` 無法區分彼此。

```js
var myMap = new Map();
myMap.set(NaN, "not a number");

myMap.get(NaN); // "not a number"

var otherNaN = Number("foo");
myMap.get(otherNaN); // "not a number"
```

### 透過 `for..of` 迭代 `Map`

Map 可以使用 `for..of` 迴圈進行迭代：

```js
var myMap = new Map();
myMap.set(0, "zero");
myMap.set(1, "one");
for (var [key, value] of myMap) {
  console.log(key + " = " + value);
}
// 0 = zero
// 1 = one

for (var key of myMap.keys()) {
  console.log(key);
}
// 0
// 1

for (var value of myMap.values()) {
  console.log(value);
}
// zero
// one

for (var [key, value] of myMap.entries()) {
  console.log(key + " = " + value);
}
// 0 = zero
// 1 = one
```

### 透過 `forEach()` 迭代 `Map`

`Map` 可以使用 `forEach()` 方法進行迭代：

```js
myMap.forEach(function (value, key) {
  console.log(key + " = " + value);
});
// Will show 2 logs; first with "0 = zero" and second with "1 = one"
```

### 與 `Array` 物件關聯

```js
var kvArray = [
  ["key1", "value1"],
  ["key2", "value2"],
];

// Use the regular Map constructor to transform a 2D key-value Array into a map
var myMap = new Map(kvArray);

myMap.get("key1"); // returns "value1"

// Use the Array.from function to transform a map into a 2D key-value Array
console.log(Array.from(myMap)); // Will show you exactly the same Array as kvArray

// Or use the keys or values iterators and convert them to an array
console.log(Array.from(myMap.keys())); // Will show ["key1", "key2"]
```

### 複製與合併 `Map`

就像 `Array` 一樣，`Map` 可以被複製:

```js
var original = new Map([[1, "one"]]);

var clone = new Map(original);

console.log(clone.get(1)); // one
console.log(original === clone); // false. Useful for shallow comparison
```

請記住，數據本身並非克隆的。

`Map` 可以被合併，保持鍵的唯一性:

```js
var first = new Map([
  [1, "one"],
  [2, "two"],
  [3, "three"],
]);

var second = new Map([
  [1, "uno"],
  [2, "dos"],
]);

// Merge two maps. The last repeated key wins.
// Spread operator essentially converts a Map to an Array
var merged = new Map([...first, ...second]);

console.log(merged.get(1)); // uno
console.log(merged.get(2)); // dos
console.log(merged.get(3)); // three
```

`Map` 也可以跟 `Array` 合併：

```js
var first = new Map([
  [1, "one"],
  [2, "two"],
  [3, "three"],
]);

var second = new Map([
  [1, "uno"],
  [2, "dos"],
]);

// Merge maps with an array. The last repeated key wins.
var merged = new Map([...first, ...second, [1, "eins"]]);

console.log(merged.get(1)); // eins
console.log(merged.get(2)); // dos
console.log(merged.get(3)); // three
```

規格
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-map-objects |

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

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

參見
--

* Map and Set bug at Mozilla
* ECMAScript Harmony proposal
* `Set`
* `WeakMap` (en-US)
* `WeakSet` (en-US)


Promise - JavaScript

Promise
=======

**`Promise`** 物件代表一個即將完成、或失敗的非同步操作，以及它所產生的值。

**備註：** 此條目為介紹 Promise 建構式。要瞭解 Promise 相關使用方式，請先參考使用 Promise。Promise 建構式主要用於包裹尚未支援 Promise 的函式。

嘗試一下
----

語法
--

```js
new Promise( /\* executor \*/ function(resolve, reject) { ... } );
```

### 參數

executor

 為一個依序接收兩個參數的函式：`resolve` 及 `reject`（實現及拒絕回呼函式）。在 Promise 實作中，`executor` 函式在傳入參數 `resolve` 與 `reject` 後會立刻執行（`executor` 函式會在 `Promise` 建構式回傳 Promise 物件前被執行）。`resolve` 與 `reject` 函式，會在被個別呼叫時，個別執行之。通常 executor 函式會發起一些非同步操作。接著，成功完成後執行 `resolve` 以完成 promise；或如果有錯誤，執行 `rejects`。
 如果 executor 函式在執行中拋出錯誤，promise 會被拒絕（rejected），回傳值也將被忽略。

描述
--

**`Promise`** 會代理一個建立時，不用預先得知的值。它使你能夠繫結（associate）著發動非同步操作後，最終的成功值（success value）或失敗訊息（failure reason）的處理函式（handlers）。這讓非同步方法回傳值的方式很像同步方法，但不是回傳最終結果：非同步方法回傳一個 *promise* 物件作為未來某時間點的值。

一個 `Promise` 物件處於以下幾種狀態：

* *擱置（pending）*：初始狀態，不是 fulfilled 與 rejected。
* *實現（fulfilled）*：表示操作成功地完成。
* *拒絕（rejected）*：表示操作失敗了。

一個處於擱置狀態的 promise 能以一個值被實現（fulfilled），或是以一個原因或錯誤而被拒絕（rejected）。當上述任一狀態轉換發生時，那些透過 `then` 方法所繫結（associated）的處理函式列隊就會依序被調用。（若一個 promise 已被實現或拒絕，繫結（attached）於它的處理函式將立即被呼叫，因此完成非同步操作與繫結處理函式之間不存在競爭條件（race condition）。）

由於 `Promise.prototype.then()` 以及 `Promise.prototype.catch()` 方法都回傳 promise，它們可以被串接。

![](https://cdn.rawgit.com/Vectaio/a76330b025baf9bcdf07cb46e5a9ef9e/raw/26c4213a93dee1c39611dcd0ec12625811b20a26/js-promise.svg)

**備註：** 許多其他語言擁有機制用來惰性求值（lazy evaluation）及延遲（deferring）運算，它們也被稱作「promises」 — e.g. Scheme. 然而在 JavaScript 中 Promises 代表那些（已經）發生中（happening）的程序，它們可以繫結回呼函式。若你要找的是惰性求值表示式，考慮不帶參數的 arrow function：`f = () => expression` 來建立惰性求值表示式，並透過 `f()` 進行求值.

**備註：** 一個被實現或拒絕，但不處於 pending 的 promise 被稱作被解決（settled）。你也會見到使用解決（resolved）一詞來描述 promises — 這代表 promises 被實現（fulfilled）了。States and fates 這篇文章包含了更多 promises 的專有名詞。

屬性
--

`Promise.length`

長度屬性，值固定為 `1`。(建構式參數數目).

`Promise.prototype` (en-US)

`Promise` 建構式的原型（prototype）.

方法
--

`Promise.all(iterable)`

回傳一個 promise，當在引數 iterable 中所有 promises 都被實現時被實現，或在引數 iterable 中有一個 promise 被拒絕時立刻被拒絕。若回傳的 promise 被實現，它將以一個實現值的陣列被實現，其順序與 iterable 中的 promises 相同。若回傳的 promise 被拒絕，它將以失敗訊息被拒絕，此訊息來自第一個在 iterable 中被拒絕的 promise。這個方法在聚集許多 promises 的結果時很有效。

`Promise.race(iterable)`

回傳一個被實現或拒絕的 promise，當 iterable 中有一個 promise 被實現或拒絕時。

`Promise.reject(reason)`

回傳一個以失敗訊息拒絕的 `promise`。

`Promise.resolve(value)`

回傳一個以 value 實現的 `promise`。若該值為 thenable (i.e. 具有 `then` 方法)，回傳的 promise 將跟隨（follow）之，採用她的最終狀態； 在其他情形回傳的 promise 將以 value 被實現。一般來說，當你不知道 value 是否為 promise，使用 `Promise.resolve(value)`，將回傳值以 promise 作處理。

`Promise` 原型
------------

### 屬性

`Promise.prototype[@@toStringTag]`

The initial value of the `@@toStringTag` (en-US) property is the string `"Promise"`. This property is used in `Object.prototype.toString()` (en-US).

### 方法

See the Microtask guide (en-US) to learn more about how these methods use the Microtask queue and services.

`Promise.prototype.catch()`

Appends a rejection handler callback to the promise, and returns a new promise resolving to the return value of the callback if it is called, or to its original fulfillment value if the promise is instead fulfilled.

`Promise.prototype.then()`

Appends fulfillment and rejection handlers to the promise, and returns a new promise resolving to the return value of the called handler, or to its original settled value if the promise was not handled (i.e. if the relevant handler `onFulfilled` or `onRejected` is not a function).

`Promise.prototype.finally()`

Appends a handler to the promise, and returns a new promise that is resolved when the original promise is resolved. The handler is called when the promise is settled, whether fulfilled or rejected.

建立 Promise
----------

一個 `Promise` 物件透過 `new` 及其建構式建立。這個建構式接收一個叫作」執行器函式（executor function）「的引數。此函式接收兩個函式作為引數。第一個函式（`resolve）`在非同步作業成功完成時，以該作業之結果值被呼叫。第二個函式（`reject`）在作業失敗時，以失敗訊息，通常是一個 error object，被呼叫。

```js
const myFirstPromise = new Promise((resolve, reject) => {
  // 執行一些非同步作業，最終呼叫:
  //
  // resolve(someValue); // 實現
  // 或
  // reject("failure reason"); // 拒絕
});
```

要提供一個函式 promise 功能，讓它回傳一個 promise 即可：

```js
function myAsyncFunction(url) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open("GET", url);
    xhr.onload = () => resolve(xhr.responseText);
    xhr.onerror = () => reject(xhr.statusText);
    xhr.send();
  });
}
```

範例
--

### 入門範例

```js
let myFirstPromise = new Promise((resolve, reject) => {
  // 當非同步作業成功時，呼叫 resolve(...),而失敗時則呼叫 reject(...)。
  // 在這個例子中，使用 setTimeout(...) 來模擬非同步程式碼。
  // 在實務中，你將可能使用像是 XHR 或者一個 HTML5 API.
  setTimeout(function () {
    resolve("Success!"); // Yay！非常順利！
  }, 250);
});

myFirstPromise.then((successMessage) => {
  // successMessage 是任何你由上方 resolve(...) 傳入的東西。
  // 在此僅作為成功訊息，但是它不一定是字串。
  console.log("Yay! " + successMessage);
});
```

### 進階範例

```html
<button id="btn">Make a promise!</button>
<div id="log"></div>
```

這個小範例演示了 `Promise` 的運作機制。每當 `<button>` (en-US) 被點擊時，`testPromise()` 方法被呼叫。每次點擊將透過 `window.setTimeout()` (en-US) 建立一個將在 1-3 秒內隨機地被實現的 promise，供 promise 計數（一個從 1 開始的數值）。建構式 `Promise()` 被用來建立 promise。

promise 的實現值單純地經由一個實現回呼函式 `p1.then()` 被印出。下以一些文字紀錄來展現方法中同步的與非同步處理 promise 的部分是如何分離彼此。

```js
"use strict";
var promiseCount = 0;

function testPromise() {
  let thisPromiseCount = ++promiseCount;

  let log = document.getElementById("log");
  log.insertAdjacentHTML(
    "beforeend",
    thisPromiseCount + ") Started (<small>Sync code started</small>)<br/>",
  );

  // 建立一個新的 promise：此 promise 承諾一個數值計數, 由 1 開始（等待約 2 秒）
  let p1 = new Promise(
    // 這個解決器函數（resolver function）呼叫實現或
    // 拒絕 promise。
    (resolve, reject) => {
      log.insertAdjacentHTML(
        "beforeend",
        thisPromiseCount +
          ") Promise started (<small>Async code started</small>)<br/>",
      );
      // 在此例子單純用來產生非同步特性。
      window.setTimeout(
        function () {
          // 實現這個 promise!
          resolve(thisPromiseCount);
        },
        Math.random() \* 2000 + 1000,
      );
    },
  );

  // 接著透過呼叫 then() 來決定 promise 進入 resolved 時，要透過 then() 做什麼，
  // 或是進入 rejected 時，要透過 catch() 方法要做什麼。
  p1.then(
    // 印出實現值（fulfillment value）
    function (val) {
      log.insertAdjacentHTML(
        "beforeend",
        val + ") Promise fulfilled (<small>Async code terminated</small>)<br/>",
      );
    },
  ).catch(
    // 印出失敗訊息（rejection reason）
    (reason) => {
      console.log("Handle rejected promise (" + reason + ") here.");
    },
  );

  log.insertAdjacentHTML(
    "beforeend",
    thisPromiseCount +
      ") Promise made (<small>Sync code terminated</small>)<br/>",
  );
}
```

譯註：resolver function 即 executor function。

```js
if ("Promise" in window) {
  let btn = document.getElementById("btn");
  btn.addEventListener("click", testPromise);
} else {
  log = document.getElementById("log");
  log.innerHTML =
    "Live example not available as your browser doesn't support the <code>Promise<code> interface.";
}
```

這個範例從點擊按鈕開始。你的瀏覽器需要支援 Promise。在短時間內點擊按鈕許多次，你甚至將看到不同的 promises 一個接一個地被實現。

使用 XHR 載入圖片
-----------

另一個使用 `Promise` and `XMLHttpRequest` 來載入圖片的簡單例子可以在 MDN GitHub js-examples 儲存庫找到。 你也可以see it in action。每個步驟都附以註解，讓你能逐步遵隨 Promise 與 XHR 架構。

規範
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-promise-objects |

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

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

參見
--

* Using promises
* Promises/A+ specification
* Venkatraman.R - JS Promise (Part 1, Basics)
* Venkatraman.R - JS Promise (Part 2 - Using Q.js, When.js and RSVP.js)
* Venkatraman.R - Tools for Promises Unit Testing
* Jake Archibald: JavaScript Promises: There and Back Again
* Domenic Denicola: Callbacks, Promises, and Coroutines – Asynchronous Programming Patterns in JavaScript
* Matt Greer: JavaScript Promises ... In Wicked Detail
* Forbes Lindesay: promisejs.org
* Nolan Lawson: We have a problem with promises — Common mistakes with promises
* Promise polyfill
* Udacity: JavaScript Promises


RangeError - JavaScript

RangeError
==========

\*\*`RangeError`\*\*物件在一個給定的值不在允許的集合或範圍內時被作為一個錯誤拋出

語法
--

```js
new RangeError([message[, fileName[, lineNumber]]])
```

### 參數

`message`

可選。具人類可讀性的錯誤說明

`fileName`
非標準

可選。包含造成錯誤發生的程式碼的檔案名稱

`lineNumber`
非標準

可選。造成錯誤發生的程式碼行號

說明
--

當試著往一個 function 傳入一個不被其允許的值作為參數時，一個`RangeError`被拋出。這可在多種情況遭遇到，例如傳入一個不被允許的字串值到 `String.prototype.normalize()` (en-US)，或試著透過 `Array` constructor 用一個不合法的長度來創建一個陣列，或往數值方法像是`Number.toExponential()`、`Number.toFixed()`、`Number.toPrecision()` (en-US) 傳進糟糕的值。

屬性
--

`RangeError.prototype` (en-US)

允許對一個 `RangeError` 物件增加其屬性。

方法
--

普遍的 `RangeError` 自身沒有包含方法，儘管他的確從原型鍊中繼承了一些。

`RangeError` 物件實體
-----------------

### 屬性

`RangeError.prototype.message` (en-US)

Error message. Inherited from `Error` (en-US).

`RangeError.prototype.name` (en-US)

Error name. Inherited from `Error` (en-US).

`RangeError.prototype.cause` (en-US)

Error cause. Inherited from `Error` (en-US).

`RangeError.prototype.fileName` (en-US)
非標準

Path to file that raised this error. Inherited from `Error` (en-US).

`RangeError.prototype.lineNumber` (en-US)
非標準

Line number in file that raised this error. Inherited from `Error` (en-US).

`RangeError.prototype.columnNumber`
非標準

Column number in line that raised this error. Inherited from `Error` (en-US).

`RangeError.prototype.stack` (en-US)
非標準

Stack trace. Inherited from `Error` (en-US).

範例
--

### 使用 `RangeError`（數值）

```js
function check(n) {
  if (!(n >= -500 && n <= 500)) {
    throw new RangeError("The argument must be between -500 and 500.");
  }
}

try {
  check(2000);
} catch (error) {
  if (error instanceof RangeError) {
    // Handle the error.
  }
}
```

### 使用 `RangeError`（非數值）

```js
function check(value) {
  if (["apple", "banana", "carrot"].includes(value) === false) {
    throw new RangeError(
      'The argument must be an "apple", "banana", or "carrot".',
    );
  }
}

try {
  check("cabbage");
} catch (error) {
  if (error instanceof RangeError) {
    // Handle the error.
  }
}
```

規範
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-native-error-types-used-in-this-standard-rangeerror |

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

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

參見
--

* `Error` (en-US)
* `RangeError.prototype` (en-US)
* `Array`
* `Number.toExponential()`
* `Number.toFixed()`
* `Number.toPrecision()` (en-US)
* `String.prototype.normalize()` (en-US)


Set - JavaScript

Set
===

**`Set`** 物件可讓你儲存任何類型的唯一值（unique），不論是基本型別（primitive）值 (en-US)或物件參考（references）。

嘗試一下
----

語法
--

```js
new Set([iterable]);
```

### 參數

`iterable`

若一個可迭代物件 (en-US)被傳入，其所有的元素將會被加入至新的 `Set`。若你沒有指定此參數，或參數值為 `null`，則新的 `Set` 會是空的。

### 回傳值

一個新的 `Set` 物件。

描述
--

`Set` 對象是數值的收集器。你可以按插入順序迭代收集器中的元素。在 `Set` 裡的元素只會出現一次\*\*；\*\* 意即在`Set`裡的元素都是獨一無二

### 值的相等性

因為在 Set 裡每個值都是獨立的，所以都會檢查值的相等性。在早期的 ECMAScript 規範版本中，此處算法跟基於===操作符中使用的算法並不相同。具體來說，在 `Set`裡+0(在嚴格模式是和-0 相等)和-0 是不同的值。然而在 ECMAScript 2015 規範中這點已被更改。請參閱 瀏覽器兼容性 中的"Value equality for -0 and 0"。

另外，NaN 和 undefined 都可以被放置在 Set 中， NaN 之間被視為相同的值（儘管 NaN !== NaN）。

`Set.length`

The value of the `length` property is 0.

`get Set[@@species]` (en-US)

The constructor function that is used to create derived objects.

`Set.prototype` (en-US)

Represents the prototype for the `Set` constructor. Allows the addition of properties to all `Set` objects.

`Set` 物件實體
----------

All `Set` instances inherit from `Set.prototype` (en-US).

### 屬性

`Set.prototype[@@toStringTag]`

The initial value of the `@@toStringTag` (en-US) property is the string `"Set"`. This property is used in `Object.prototype.toString()` (en-US).

`Set.prototype.size` (en-US)

Returns the number of values in the `Set` object.

### 方法

`Set.prototype.add()`

Inserts a new element with a specified value in to a `Set` object, if there isn't an element with the same value already in the `Set`.

`Set.prototype.clear()`

Removes all elements from the `Set` object.

`Set.prototype.delete()`

Removes the element associated to the `value` and returns a boolean asserting whether an element was successfully removed or not. `Set.prototype.has(value)` will return `false` afterwards.

`Set.prototype.has()`

Returns a boolean asserting whether an element is present with the given value in the `Set` object or not.

`Set.prototype[@@iterator]()` (en-US)

Returns a new iterator object that yields the **values** for each element in the `Set` object in insertion order.

`Set.prototype.values()`

Returns a new iterator object that yields the **values** for each element in the `Set` object in insertion order.

`Set.prototype.keys()` (en-US)

An alias for `Set.prototype.values()`.

`Set.prototype.entries()`

Returns a new iterator object that contains **an array of `[value, value]`** for each element in the `Set` object, in insertion order.

This is similar to the `Map` object, so that each entry's *key* is the same as its *value* for a `Set`.

`Set.prototype.forEach()` (en-US)

Calls `callbackFn` once for each value present in the `Set` object, in insertion order. If a `thisArg` parameter is provided, it will be used as the `this` value for each invocation of `callbackFn`.

範例
--

### 使用 `Set` 物件

```js
var mySet = new Set();

mySet.add(1); // Set [ 1 ]
mySet.add(5); // Set [ 1, 5 ]
mySet.add(5); // Set [ 1, 5 ]
mySet.add("some text"); // Set [ 1, 5, 'some text' ]
var o = { a: 1, b: 2 };
mySet.add(o);

mySet.add({ a: 1, b: 2 }); // o is referencing a different object so this is okay

mySet.has(1); // true
mySet.has(3); // false, 3 has not been added to the set
mySet.has(5); // true
mySet.has(Math.sqrt(25)); // true
mySet.has("Some Text".toLowerCase()); // true
mySet.has(o); // true

mySet.size; // 5

mySet.delete(5); // removes 5 from the set
mySet.has(5); // false, 5 has been removed

mySet.size; // 4, we just removed one value
console.log(mySet); // Set [ 1, "some text", Object {a: 1, b: 2}, Object {a: 1, b: 2} ]
```

### 迭代 Sets

```js
// iterate over items in set
// logs the items in the order: 1, "some text", {"a": 1, "b": 2}, {"a": 1, "b": 2}
for (let item of mySet) console.log(item);

// logs the items in the order: 1, "some text", {"a": 1, "b": 2}, {"a": 1, "b": 2}
for (let item of mySet.keys()) console.log(item);

// logs the items in the order: 1, "some text", {"a": 1, "b": 2}, {"a": 1, "b": 2}
for (let item of mySet.values()) console.log(item);

// logs the items in the order: 1, "some text", {"a": 1, "b": 2}, {"a": 1, "b": 2}
//(key and value are the same here)
for (let [key, value] of mySet.entries()) console.log(key);

// convert Set object to an Array object, with Array.from
var myArr = Array.from(mySet); // [1, "some text", {"a": 1, "b": 2}, {"a": 1, "b": 2}]

// the following will also work if run in an HTML document
mySet.add(document.body);
mySet.has(document.querySelector("body")); // true

// converting between Set and Array
mySet2 = new Set([1, 2, 3, 4]);
mySet2.size; // 4
[...mySet2]; // [1, 2, 3, 4]

// intersect can be simulated via
var intersection = new Set([...set1].filter((x) => set2.has(x)));

// difference can be simulated via
var difference = new Set([...set1].filter((x) => !set2.has(x)));

// Iterate set entries with forEach
mySet.forEach(function (value) {
  console.log(value);
});

// 1
// 2
// 3
// 4
```

### 實作基本的 set 操作

```js
Set.prototype.isSuperset = function (subset) {
  for (var elem of subset) {
    if (!this.has(elem)) {
      return false;
    }
  }
  return true;
};

Set.prototype.union = function (setB) {
  var union = new Set(this);
  for (var elem of setB) {
    union.add(elem);
  }
  return union;
};

Set.prototype.intersection = function (setB) {
  var intersection = new Set();
  for (var elem of setB) {
    if (this.has(elem)) {
      intersection.add(elem);
    }
  }
  return intersection;
};

Set.prototype.difference = function (setB) {
  var difference = new Set(this);
  for (var elem of setB) {
    difference.delete(elem);
  }
  return difference;
};

//Examples
var setA = new Set([1, 2, 3, 4]),
  setB = new Set([2, 3]),
  setC = new Set([3, 4, 5, 6]);

setA.isSuperset(setB); // => true
setA.union(setC); // => Set [1, 2, 3, 4, 5, 6]
setA.intersection(setC); // => Set [3, 4]
setA.difference(setC); // => Set [1, 2]
```

### 與 `Array` 物件關聯

```js
var myArray = ["value1", "value2", "value3"];

// Use the regular Set constructor to transform an Array into a Set
var mySet = new Set(myArray);

mySet.has("value1"); // returns true

// Use the spread operator to transform a set into an Array.
console.log([...mySet]); // Will show you exactly the same Array as myArray
```

### 與 `Strings` 關聯

```js
var text = "India";

var mySet = new Set(text); // Set ['I', 'n', 'd', 'i', 'a']
mySet.size; // 5
```

規範
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-set-objects |

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

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

參見
--

* `Map`
* `WeakMap` (en-US)
* `WeakSet` (en-US)


Math - JavaScript

Math
====

**`Math`** 是一個擁有數學常數及數學函數（非函式物件）屬性及方法的內建物件。

描述
--

不像其他的全域物件，`Math` 並非建構函式。所有 `Math` 的屬性及方法皆為靜態。你可以使用 `Math.PI` 來參考到圓周率 pi 的常數值，以及可以呼叫 `Math.sin(x)` 函式來計算三角函數正弦曲線 sine（`x` 為方法的引數）。常數是由 JavaScript 中實數的完整精度來定義。

屬性
--

`Math.E` (en-US)

歐拉常數 (此指自然常數) ，也是自然對數的底數，約為 2.718。

`Math.LN2` (en-US)

2 的自然對數，約為 0.693。

`Math.LN10` (en-US)

10 的自然對數，約為 2.303。

`Math.LOG2E` (en-US)

以 2 為底的 E 的對數，約為 1.443。

`Math.LOG10E` (en-US)

以 10 為底的 E 的對數，約為 0.434。

`Math.PI` (en-US)

一個圓的圓周和其直徑比值，約為 3.14159。

`Math.SQRT1_2` (en-US)

1/2 的平方根；也就是 1 除以 2 的平方根，約為 0.707。

`Math.SQRT2` (en-US)

2 的平方根，約為 1.414。

方法
--

**備註：** 三角函數 (`sin()`, `cos()`, `tan()`, `asin()`, `acos()`, `atan()`, `atan2()`) 的參數或回傳值的角度皆以弧度為單位。把角度乘上 `(Math.PI / 180)` 會得到弧度單位，將弧度除以該數則會轉換回一般所用的角度單位。

**備註：** 許多數學方法的精度是取決於實作方式的。這意味著不同的瀏覽器可能會得到不同的結果，甚至同一個 JS 引擎在不同的作業系統或架構上所得到的結果都有可能相異。

`Math.abs(x)` (en-US)

回傳 x 的絕對值。

`Math.acos(x)` (en-US)

回傳 x 的反餘弦值。

`Math.acosh(x)` (en-US)

Returns the hyperbolic arccosine of a number.

`Math.asin(x)` (en-US)

回傳 x 的反正弦值。

`Math.asinh(x)` (en-US)

Returns the hyperbolic arcsine of a number.

`Math.atan(x)` (en-US)

回傳 x 的反正切值。

`Math.atanh(x)` (en-US)

Returns the hyperbolic arctangent of a number.

`Math.atan2(y, x)` (en-US)

Returns the arctangent of the quotient of its arguments.

`Math.cbrt(x)` (en-US)

回傳 x 的立方根值。

`Math.ceil(x)`

回傳不小於 x 的最小整數值。

`Math.clz32(x)` (en-US)

Returns the number of leading zeroes of a 32-bit integer.

`Math.cos(x)` (en-US)

回傳 x 的餘弦值。

`Math.cosh(x)` (en-US)

Returns the hyperbolic cosine of a number.

`Math.exp(x)` (en-US)

回傳 E^x，x 為給定數值，E 為歐拉常數 (自然對數的底數)。

`Math.expm1(x)` (en-US)

回傳 `exp(x)` 減去 1 的值。

`Math.floor(x)`

回傳不大於 x 的最大整數值。

`Math.fround(x)` (en-US)

Returns the nearest single precision float representation of a number.

`Math.hypot([x[, y[, …]]])` (en-US)

回傳參數平方之和的平方根。

`Math.imul(x, y)` (en-US)

Returns the result of a 32-bit integer multiplication.

`Math.log(x)` (en-US)

回傳 x 的自然對數值。

`Math.log1p(x)` (en-US)

回傳 `1 + x` 的自然對數值。

`Math.log10(x)` (en-US)

回傳以 10 為底，x 的對數值。

`Math.log2(x)` (en-US)

回傳以 2 為底，x 的對數值。

`Math.max([x[, y[, …]]])` (en-US)

回傳給定數值中的最大值。

`Math.min([x[, y[, …]]])` (en-US)

回傳給定數值中的最小值。

`Math.pow(x, y)` (en-US)

回傳 x 的 y 次方，也就是 `x^y`。

`Math.random()`

回傳一個 0 到 1 之間的偽隨機值。

`Math.round(x)`

回傳 x 的四捨五入值。

`Math.sign(x)` (en-US)

回傳 x 的正負號，也就是回傳 x 的正負。

`Math.sin(x)` (en-US)

回傳 x 的正弦值。

`Math.sinh(x)` (en-US)

Returns the hyperbolic sine of a number.

`Math.sqrt(x)` (en-US)

回傳 x 的正平方根。

`Math.tan(x)` (en-US)

回傳 x 的正切值。

`Math.tanh(x)` (en-US)

Returns the hyperbolic tangent of a number.

`Math.toSource()`
非標準

回傳字串 `"Math"`。

`Math.trunc(x)` (en-US)

Returns the integral part of the number x, removing any fractional digits.

擴充 `Math` 物件
------------

As most of the built-in objects in JavaScript, the `Math` object can be extended with custom properties and methods. To extend the `Math` object, you do not use 'prototype'. Instead, you directly extend `Math`:
```

Math.propName = propValue;
Math.methodName = methodRef;
```

For instance, the following example adds a method to the `Math` object for calculating the *greatest common divisor* of a list of arguments.

```js
/\* Variadic function -- Returns the greatest common divisor of a list of arguments \*/
Math.gcd = function () {
  if (arguments.length == 2) {
    if (arguments[1] == 0) return arguments[0];
    else return Math.gcd(arguments[1], arguments[0] % arguments[1]);
  } else if (arguments.length > 2) {
    var result = Math.gcd(arguments[0], arguments[1]);
    for (var i = 2; i < arguments.length; i++)
      result = Math.gcd(result, arguments[i]);
    return result;
  }
};
```

Try it:

```js
console.log(Math.gcd(20, 30, 15, 70, 40)); // `5`
```

規範
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-math-object |

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

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

參見
--

* `Number`


Date - JavaScript

Date
====

建立一個 JavaScript **`Date`** 物件來指向某一個時間點。Date 物件是基於世界標準時間（UTC） 1970 年 1 月 1 日開始的毫秒數值來儲存時間。

語法
--
```

new Date();
new Date(value);
new Date(dateString);
new Date(year, month[, day[, hour[, minutes[, seconds[, milliseconds]]]]]);
```

**備註：** JavaScript `Date` 物件只能由以 Date 作為建構子來產生，如果把 Date 作為一般的函數來呼叫（省略掉 `new` 運算子）將會得到一個字串而非 Date 物件；與其它 JavaScript 物件不同，它並沒有物件實體語法（例如：以中刮號 [ ] 表示陣列的宣告方式）。

### 參數

**備註：** 當傳入超過一個參數到 Date 建構子，若參數值超過它的合理範圍（例如：傳數值 13 到月份，或傳數值 70 給分鐘），相鄰的參數值將會被調整。例如： `new Date(2013, 13, 1)` 將等同於 `new Date(2014, 1, 1)` 都會建立代表 `2014-02-01` 的物件（注意月份值由 0 開始）。同樣的， `new Date(2013, 2, 1, 0, 70)` 與 `new Date(2013, 2, 1, 1, 10)` 一樣會建立代表 `2013-03-01T01:10:00` 的 Date 物件。

**備註：** 當傳入超過一個參數到 Date 建構子，所指定的參數值會視為本地時間。如果希望指定的值為世界標準時間（UTC），則應使用 `new Date(`Date.UTC(...)`)` 語法，傳入一樣格式的參數。

`value`

自世界標準時間（UTC） 1970 年 1 月 1 日 00:00:00 開始的毫秒整數（Integer）值（Unix 紀元；但要注意到大多 Unix 時間戳記是以秒而非毫秒為單位）。

`dateString`

表示時間日期的字串。這個字串應該要能被 `Date.parse()` 方法解析（符合 IETF-compliant RFC 2822 timestamps 及 version of ISO8601 格式要求).

**備註：** 由於各家瀏覽器有所不同且具有差異性，因此非常不鼓勵使用 Date 建構子（或 `Date.parse` 方法，它們是同等的）來解析時間日期字串。

`year`

表示年份的整數。當數值落在 0 到 99 之間，表示 1900 到 1999 之間的年份。參考下面的範例.

`month`

表示月份的整數。由 0 開始（一月）到 11 （十二月）。

`day`

選用。表示月份中第幾天的整數值。

`hour`

選用。表示小時數的整數值。

`minute`

選用。表示分鐘數的整數值。

`second`

選用。表示秒數的整數值。

`millisecond`

選用。表示毫秒數的整數值。

描述
--

* 如果沒有傳入任務參數到建構子，會依系統設定建立出代表當下時間的 Date 物件。
* 如果傳入至少兩個參數，缺少日期的話會設為 1，其它參數則會被設定為 0。
* JavaScript 的 date 基於世界標準時間（UTC）1970 年 1 月 1 日午夜的毫秒數。一天有 86,400,000 毫秒。JavaScript `Date` 物件可表示的範圍由世界標準時間（UTC） 1970 年 1 月 1 日為基準的 -100,000,000 天到 100,000,000 天。
* JavaScript `Date` 物件提供跨平台一致的行為。這個時間數值可以在系統之間傳遞表示相同的時間，如果建立本地的時間物件，其表現將會與本地習慣相對應。
* JavaScript `Date` 物件提供了若干 UTC (通用的) 以及本地時間方法。UTC，也被稱為格林威治標準時間（GMT）被指定作為世界時間的標準。本地時間指的是被設定在執行 JavaScript 電腦上的時間。
* 以函數方式呼叫 `Date` （也就是省略 `new` 建構子）將會回傳一個表示當下時間日期的字串。
屬性
--

`Date.prototype` (en-US)

允許填加屬於到 JavaScript `Date` 物件。

`Date.length`

`Date.length` 的值為 7。這是建構子能夠處理的參數數量。

方法
--

`Date.now()`

回傳對應於當下時間的數值 - 1970/01/01 00:00:00 (UTC) 到當下的毫秒數。

`Date.parse()`

解析字串所表示的時間，回傳由 1970/01/01 00:00:00 (UTC) 到該時間的毫秒數值。

**備註：** 由於瀏覽器之間的不同與差異，強烈不建議使用 `Date.parse` 。

`Date.UTC()`

需要傳入與建構子相同的參數數目（即 2 到 7 個），會得到由 1970-01-01 00:00:00 UTC 到該日期時間的毫秒數。（輸入的參數會視為世界標準時間，而非本地時間）

JavaScript `Date` 物件實體
----------------------

所有 `Date` 物件實體繼承自 `Date.prototype` (en-US) 。這個 Date 建構子的 prototype 物件可以被修改以影響所有 Date 物件實體。

### Date.prototype 方法

`Date.now()`

回傳對應於當下時間的數值 - 1970/01/01 00:00:00 (UTC) 到當下的毫秒數。

`Date.parse()`

解析字串所表示的時間，回傳由 1970/01/01 00:00:00 (UTC) 到該時間的毫秒數值。

**備註：** 由於瀏覽器之間的不同與差異，強烈不建議使用 `Date.parse` 。

`Date.UTC()`

需要傳入與建構子相同的參數數目（即 2 到 7 個），會得到由 1970-01-01 00:00:00 UTC 到該日期時間的毫秒數。（輸入的參數會視為世界標準時間，而非本地時間）

範例
--

### 幾種建立 Date 物件的方式

接下來的幾個範例，展示幾種建立 Date 物件的方式：

**備註：** 由於瀏覽器之間的不同與差異，強烈不建議使用解析字串的方式建立 Date 物件。

```js
var today = new Date();
var birthday = new Date("December 17, 1995 03:24:00");
var birthday = new Date("1995-12-17T03:24:00");
var birthday = new Date(1995, 11, 17);
var birthday = new Date(1995, 11, 17, 3, 24, 0);
```

### 兩位數的年份對應到 1900 - 1999

為了建立及取得西元 0 到 99 的日期，應該使用 `Date.prototype.setFullYear()` (en-US) 以及 `Date.prototype.getFullYear()` (en-US) 方法。

```js
var date = new Date(98, 1); // Sun Feb 01 1998 00:00:00 GMT+0000 (GMT)

// 過時的方法，98 在這裡對應到 1998 年
date.setYear(98); // Sun Feb 01 1998 00:00:00 GMT+0000 (GMT)

date.setFullYear(98); // Sat Feb 01 0098 00:00:00 GMT+0000 (BST)
```

### 計算執行時間

下面的例子展示如何使用兩個 Date 物件來求得執行程式所花費毫秒數。

由於日（在夏令時轉換時）、月及年的長度並非固定，如果表示經過時間採用時、分、秒以外的單位，需要對這些差異作深入的研究，以處理可能發生的問題。

```js
// 使用 Date 物件
var start = Date.now();

// 要計算執行時間的程式放在這裡
doSomethingForALongTime();
var end = Date.now();
var elapsed = end - start; // 執行程式經過的毫秒數
```

```js
// 使用內建方法
var start = new Date();

// 要計算執行時間的程式放在這裡
doSomethingForALongTime();
var end = new Date();
var elapsed = end.getTime() - start.getTime(); // 執行程式經過的毫秒數
```

```js
// 測試一個函數執行時間，並返回其回傳值
function printElapsedTime(fTest) {
  var nStartTime = Date.now(),
    vReturn = fTest(),
    nEndTime = Date.now();

  console.log(
    "Elapsed time: " + String(nEndTime - nStartTime) + " milliseconds",
  );
  return vReturn;
}

yourFunctionReturn = printElapsedTime(yourFunction);
```

**備註：** 在瀏覽器支援 Web Performance API (en-US) 高精度特性下， `Performance.now()` (en-US) 可以提供比 `Date.now()` 更可靠、精確的執行時間測試結果。

規範
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-date-objects |

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

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

[1] 一些瀏覽器在解析日期時間會發生問題： 3/14/2012 blog from danvk Comparing FF/IE/Chrome on Parsing Date Strings

[2] ISO8601 Date Format is not supported in Internet Explorer 8, and other version can have issues when parsing dates


Number - JavaScript

Number
======

**`Number`** JavaScript 物件是允許你操作數值的包覆物件。`Number` 物件是以 `Number()` 建構子來建立的。

語法
--

```js
new Number(value)
```

### 參數

`value`

用來建立物件的數值。

說明
--

`Number` 物件主要的用途：

* 如果參數沒辦法被轉換成數字，則它會回傳 `NaN` 。
* 在不是使用建構式的情境中(即不用 `new` 運算子), `Number` 可以被用來轉換型別.
屬性
--

`Number.EPSILON` (en-US)

介於 1 和大於 1 的最小值之可表示的差。

`Number.MAX_SAFE_INTEGER` (en-US)

JavaScript 中 IEEE-754 雙精度範圍間的最大整數 (`2^53 - 1`) 。

`Number.MAX_VALUE` (en-US)

可表示的最大正整數。

`Number.MIN_SAFE_INTEGER` (en-US)

JavaScript 中 IEEE-754 雙精度範圍間的最小整數 (`-(2^53 - 1)`) 。

`Number.MIN_VALUE` (en-US)

可表示的最小值，即最靠近 0 的正整數?(`5.00×103245.00\times10^{324}`)。

`Number.NaN` (en-US)

特別用來表示**非數值**的物件。

`Number.NEGATIVE_INFINITY` (en-US)

特別用來表示**負無窮**的數值。

`Number.POSITIVE_INFINITY` (en-US)

特別用來表示**正無窮**的數值。

`Number.prototype` (en-US)

允許被添加到 `Number` 物件的屬性。

方法
--

`Number.isNaN()` (en-US)

判斷傳入的值是不是 NaN.

`Number.isFinite()` (en-US)

判斷傳入的值是不是一個有限的數值。

`Number.isInteger()` (en-US)

判斷傳入的值是不是一個整數。

`Number.isSafeInteger()` (en-US)

判斷傳入的值是不是在 IEEE-754 雙精度範圍間 (即介於 `-(2^53 - 1)` 和 `2^53 - 1`之前)。

`Number.parseFloat()` (en-US)

這個方法和全域物件的 `parseFloat()` (en-US) 相同。

`Number.parseInt()` (en-US)

這個方法和全域物件的 `parseInt()` 相同。

`Number` 實體
-----------

所有 `Number` 實體都會繼承其建構式的 `Number.prototype` (en-US)。`Number` 的原型物件可以被修改並作用在所有 `Number` 實體。

### 方法

`Number.isNaN()` (en-US)

判斷傳入的值是不是 NaN.

`Number.isFinite()` (en-US)

判斷傳入的值是不是一個有限的數值。

`Number.isInteger()` (en-US)

判斷傳入的值是不是一個整數。

`Number.isSafeInteger()` (en-US)

判斷傳入的值是不是在 IEEE-754 雙精度範圍間 (即介於 `-(2^53 - 1)` 和 `2^53 - 1`之前)。

`Number.parseFloat()` (en-US)

這個方法和全域物件的 `parseFloat()` (en-US) 相同。

`Number.parseInt()` (en-US)

這個方法和全域物件的 `parseInt()` 相同。

範例
--

### 使用 `Number` 物件去指派值給數值變數

下列的範例使用 `Number` 物件的屬性去指派值給數個數值變數：

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

### `Number` 的整數範圍

下面的範例展示了最小和最大的整數，其可以被表示成 `Number` 物件(細節請參考 ECMAScript standard, chapter *8.5 The Number Type*)：

```js
var biggestInt = 9007199254740992;
var smallestInt = -9007199254740992;
```

當在解析已經被序列化的 JSON 的資料時，填入這個範圍之外的整數並且 JSON 剖析器強制將其轉成 `Number` 型別造成損壞是可預期的。將範圍之外的正數換成以 `String` 表示反倒是一個可行的替代方案。

### 使用 `Number` 轉換 `Date` 物件為 Unix 時間戳記

下面的範例將 `Number` 視為函式，並且使用它將 `Date` 轉換成時間戳記：

```js
var d = new Date("December 17, 1995 03:24:00");
console.log(Number(d)); // 819199440000
```

### 轉換數值字串成數值

```js
Number("123"); // 123
Number("12.3"); // 12.3
Number(""); // 0
Number("0x11"); // 17
Number("0b11"); // 3
Number("0o11"); // 9
Number("foo"); // NaN
Number("100a"); // NaN
```

規範
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-number-objects |

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

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

參見
--

* `NaN`
* The `Math` global object


NaN - JavaScript

NaN
===

全域屬性 **`NaN`** 表示「非數值」（Not-A-Number）的數值。

| `NaN` 的屬性特性 |
| --- |
| 可寫 | 否 |
| 可列舉 | 否 |
| 可配置 | 否 |

嘗試一下
----

語法
--
```

NaN
```

描述
--

`NaN` 的屬性屬於*全域物件*。

如同 `Number.NaN` (en-US) 一般，`NaN` 的初始數值是「非數值」。在當今的瀏覽器中，`NaN` 屬性不可設定（non-configurable）也不可覆寫（non-writable）。雖然可能有例外，也請不要覆蓋它。

寫程式很少會直接動用 `NaN`。通常是在 `Math` 函式計算失敗（`Math.sqrt(-1)`）或函式解析數字失敗（`parseInt("blabla")`）後才會回傳。

### 偵測是否為 `NaN`

`NaN` 不等於（`==`、`!=`、`===`、`!==`）任何值，包括 NaN 本身。請使用 `Number.isNaN()` (en-US) 或 `isNaN()` 來確認某個數值是否為 NaN。Or perform a self-comparison: NaN, and only NaN, will compare unequal to itself.

```js
NaN === NaN; // false
Number.NaN === NaN; // false
isNaN(NaN); // true
isNaN(Number.NaN); // true

function valueIsNaN(v) {
  return v !== v;
}
valueIsNaN(1); // false
valueIsNaN(NaN); // true
valueIsNaN(Number.NaN); // true
```

但請注意 `isNaN()` 與 `Number.isNaN()` 之間是有區別的：前者會在目前數字是 `NaN` 的時候回傳 `true`，或在裡面包藏一個號碼後變成 `NaN`；而後者，只有在數值是 `NaN` 的時候才會回傳 `true`。

規範
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-value-properties-of-the-global-object-nan |

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

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

參見
--

* `Number.NaN` (en-US)
* `Number.isNaN()` (en-US)
* `isNaN()`


Array - JavaScript

Array
=====

JavaScript 中的 **`Array`** 全域物件被用於建構陣列；陣列為高階（high-level）、似列表（list-like）的物件。陣列在 Javascript 裡面並沒有固定的長度與型別。由於陣列的長度可以隨時被改變，所以並不能保證陣列的密度。這取決於開發者如何使用陣列。一般來說，這是個非常方便的特性，但如果這並不適用於你的開發工作，你也許會考慮使用型別陣列。

語法
--

```js
[element0, element1, ..., elementN]
new Array(element0, element1[, ...[, elementN]])
new Array(arrayLength)
```

### 參數

`elementN`

除了只傳遞一個參數給 `Array` 構造函數，且該參數為一個數字的情況（詳見下方的 arrayLength 參數），JavaScript 陣列會以傳入的元素進行初始化。請注意，這種特殊情況僅適用於以 `Array` 構造函數建立的 JavaScript 陣列，而不適用於以括號語法建立的陣列常值（Array Literals）。

`arrayLength`

如果傳遞給 `Array` 構造函數的唯一參數是 0 和 2^32 - 1（含）之間的整數，將回傳一個新的 JavaScript 陣列，其長度被設定為這個數字。如果參數是任何其他數值，將拋出 `RangeError` 異常。

說明
--

Array（「陣列」）是類似列表（list）的物件（Object），它們的原型（Prototype）擁有方法（methods）來執行遍歷和變異操作。JavaScript 陣列的長度（元素數量），以及其元素的類型都不是固定的。取決於工程師如何選擇使用陣列，可以隨時更改陣列的長度，也可不連續儲存資料， 所以並不保證這些資料是集中的。一般情況下，這些特性很方便使用；但若這些功能都不符合你的用途，你可能會想使用型別陣列（typed arrays）。

有些人認為即便會發生警告，仍然不應該使用關聯陣列，而應該使用 `objects` (en-US)。你可參考輕量級 JavaScript 字典當中的範例。

### 存取陣列元素

JavaScript 陣列是 zero-indexed：陣列元素的索引值編排從 0 開始，而最後一個元素的索引值等同於陣列的 `length` 屬性減 1。

```js
var arr = ["this is the first element", "this is the second element"];
console.log(arr[0]); // 紀錄出 'this is the first element'
console.log(arr[1]); // 記錄出 'this is the second element'
console.log(arr[arr.length - 1]); // 記錄出 'this is the second element'
```

Array 元素同時也是物件的屬性，與 `toString` 是一種屬性相同。但若要透過下面這種方式存取陣列元素，因為屬性名稱無效的關係，會發生語法錯誤：

```js
console.log(arr.0); // 語法錯誤
```

會造成如此的原因沒有什麼特別的，在 JavaScript 當中無法用小數點的方式來參照一個名稱開頭為數字的屬性，而必須括號的表示方式來存取。舉例來說，若你有個物件的屬性名稱為「`3d`」，就只能用括號的方式來參照。

請看下列範例：

```js
var years = [1950, 1960, 1970, 1980, 1990, 2000, 2010];
console.log(years.0);   // 語法錯誤
console.log(years[0]);  // 程式正常
```

```js
renderer.3d.setTexture(model, 'character.png');     // 語法錯誤
renderer['3d'].setTexture(model, 'character.png');  // 程式正常
```

注意：以這個 `'3d'` 例子來說，必須用引號將 `3d` 包起來。你也可以將 JavaScript 陣列的索引用引號包起來（例如使用 `years['2']` 而不用 `years[2]`），但這不是必要的。JavaScript 會透過隱含的 `toString`，將 `years[2]` 當中的 2 強制轉換為字串。由於這個原因，`'2'` 與 `'02'` 會參照到 `years` 物件中的不同項目，下列程式範例結果可能回傳 `true`：

```js
console.log(years["2"] != years["02"]);
```

另一種類似的情況是，物件屬性剛好與保留字（！）相同的情況。這種情況下僅能透過括號表示方式當中的字串常值來存取：

```js
var promise = {
  var: "text",
  array: [1, 2, 3, 4],
};

console.log(promise["var"]);
```

### `length` 與數值屬性的關係

JavaScript 陣列的 `length` 屬性和其數值屬性相關。許多陣列的方法被呼叫時會參考 `length` 屬性的值（例如 `join`、`slice`、`indexOf` 等）。而有另一些方法則會去改變 `length` 屬性的值，如 `push`、`splice`。

```js
var fruits = [];
fruits.push("banana", "apple", "peach");

console.log(fruits.length); // 3
```

如果給陣列設定一個數值屬性，其值為有效但超過當下範圍的陣列 index，JavaScript 引擎會依照此數值更新陣列的 `length` 屬性：

```js
fruits[5] = "mango";
console.log(fruits[5]); // 'mango'
console.log(Object.keys(fruits)); // ['0', '1', '2', '5']
console.log(fruits.length); // 6
```

提高 `length` 屬性。

```js
fruits.length = 10;
console.log(Object.keys(fruits)); // ['0', '1', '2', '5']
console.log(fruits.length); // 10
```

降低 `length` 屬性則會刪除陣列元素。

```js
fruits.length = 2;
console.log(Object.keys(fruits)); // ['0', '1']
console.log(fruits.length); // 2
```

在 `Array.length` 頁面裡有進一步解釋。

### 使用 match 回傳結果來建立陣列

在字串與正規表示式之間的比對結果會產生一個 javascript 陣列。此陣列內含關於比對資訊的屬性與元素。 這樣的陣列由`RegExp.exec` (en-US), `String.match` (en-US), 和 `String.replace` (en-US) 所產生。參考以下範例和表格，會有助於說明這些屬性和元素：

```js
// 比對一個字元 d，後面接著一或多個 b，再接著一個 d
// Remember matched b's and the following d
// 忽略大小寫

var myRe = /d(b+)(d)/i;
var myArray = myRe.exec("cdbBdbsbz");
```

這項比對結果的屬性與元素參考如下：

| 屬性/元素 | 說明 | 範例 |
| --- | --- | --- |
| `input` | 唯讀屬性，代表 正規表示式用以比對的原始字串。 | cdbBdbsbz |
| `index` | 唯讀屬性，代表在字串中比對得到的索引，是以零為基礎(從 0 開始)。 | 1 |
| `[0]` | 一個唯獨元素以表示最後符合的字串 | dbBd |
| `[1], ...[n]` | Read-only elements that specify the parenthesized substring matches, if included in the regular expression. The number of possible parenthesized substrings is unlimited. | [1]: bB [2]: d |

屬性
--

Array.length

`Array` 建構子的長度為 1。

`get Array[@@species]` (en-US)

用來建立衍生物件的建構函數。

`Array.prototype`

可加入屬性至所有陣列物件。

方法
--

`Array.from()`

用類似陣列或可列舉物件，來建立新的 `Array` 實例。

`Array.isArray()`

若變數是陣列就回傳 true，否則回傳 false。

`Array.of()`

用可變數量的引數來建立新的 `Array` 實例，不論引數的數量或型別。

`Array` 實例
----------

所有的陣列實例都繼承自 `Array.prototype`。若修改這個陣列建構子 (Array constructor) 的原型物件 (prototype object)，將會影響所有的陣列實體。

### 屬性

`Array.prototype.length`

Reflects the number of elements in an array.

`Array.prototype[@@unscopables]` (en-US)

Contains property names that were not included in the ECMAScript standard prior to the ES2015 version and that are ignored for `with` (en-US) statement-binding purposes.

### 方法

`Array.prototype.at()` (en-US)

Returns the array item at the given index. Accepts negative integers, which count back from the last item.

`Array.prototype.concat()`

Returns a new array that is the calling array joined with other array(s) and/or value(s).

`Array.prototype.copyWithin()`

Copies a sequence of array elements within an array.

`Array.prototype.entries()`

Returns a new *array iterator* (en-US) object that contains the key/value pairs for each index in an array.

`Array.prototype.every()` (en-US)

Returns `true` if every element in the calling array satisfies the testing function.

`Array.prototype.fill()`

Fills all the elements of an array from a start index to an end index with a static value.

`Array.prototype.filter()`

Returns a new array containing all elements of the calling array for which the provided filtering function returns `true`.

`Array.prototype.find()`

Returns the value of the first element in the array that satisfies the provided testing function, or `undefined` if no appropriate element is found.

`Array.prototype.findIndex()`

Returns the index of the first element in the array that satisfies the provided testing function, or `-1` if no appropriate element was found.

`Array.prototype.findLast()` (en-US)

Returns the value of the last element in the array that satisfies the provided testing function, or `undefined` if no appropriate element is found.

`Array.prototype.findLastIndex()` (en-US)

Returns the index of the last element in the array that satisfies the provided testing function, or `-1` if no appropriate element was found.

`Array.prototype.flat()`

Returns a new array with all sub-array elements concatenated into it recursively up to the specified depth.

`Array.prototype.flatMap()` (en-US)

Returns a new array formed by applying a given callback function to each element of the calling array, and then flattening the result by one level.

`Array.prototype.forEach()` (en-US)

Calls a function for each element in the calling array.

`Array.prototype.group()` (en-US)
實驗性質

Groups the elements of an array into an object according to the strings returned by a test function.

`Array.prototype.groupToMap()` (en-US)
實驗性質

Groups the elements of an array into a `Map` according to values returned by a test function.

`Array.prototype.includes()`

Determines whether the calling array contains a value, returning `true` or `false` as appropriate.

`Array.prototype.indexOf()`

Returns the first (least) index at which a given element can be found in the calling array.

`Array.prototype.join()`

Joins all elements of an array into a string.

`Array.prototype.keys()`

Returns a new *array iterator* (en-US) that contains the keys for each index in the calling array.

`Array.prototype.lastIndexOf()`

Returns the last (greatest) index at which a given element can be found in the calling array, or `-1` if none is found.

`Array.prototype.map()`

Returns a new array containing the results of invoking a function on every element in the calling array.

`Array.prototype.pop()`

Removes the last element from an array and returns that element.

`Array.prototype.push()`

Adds one or more elements to the end of an array, and returns the new `length` of the array.

`Array.prototype.reduce()`

Executes a user-supplied "reducer" callback function on each element of the array (from left to right), to reduce it to a single value.

`Array.prototype.reduceRight()` (en-US)

Executes a user-supplied "reducer" callback function on each element of the array (from right to left), to reduce it to a single value.

`Array.prototype.reverse()`

Reverses the order of the elements of an array *in place*. (First becomes the last, last becomes first.)

`Array.prototype.shift()`

Removes the first element from an array and returns that element.

`Array.prototype.slice()`

Extracts a section of the calling array and returns a new array.

`Array.prototype.some()` (en-US)

Returns `true` if at least one element in the calling array satisfies the provided testing function.

`Array.prototype.sort()`

Sorts the elements of an array in place and returns the array.

`Array.prototype.splice()`

Adds and/or removes elements from an array.

`Array.prototype.toLocaleString()` (en-US)

Returns a localized string representing the calling array and its elements. Overrides the `Object.prototype.toLocaleString()` (en-US) method.

`Array.prototype.toString()`

Returns a string representing the calling array and its elements. Overrides the `Object.prototype.toString()` (en-US) method.

`Array.prototype.unshift()`

Adds one or more elements to the front of an array, and returns the new `length` of the array.

`Array.prototype.values()`

Returns a new *array iterator* (en-US) object that contains the values for each index in the array.

`Array.prototype[@@iterator]()`

An alias for the `values()` method by default.

範例
--

### 範例：建立陣列

以下範例會產生長度為 0 的 `msgArray` 陣列，然後指派字串值到 `msgArray[0]` 及 `msgArray[99]`，使陣列的長度變為 100。

```js
var msgArray = [];
msgArray[0] = "Hello";
msgArray[99] = "world";

if (msgArray.length === 100) {
  console.log("The length is 100.");
}
```

### （透過索引）取得陣列項目

```js
var first = fruits[0];
// Apple

var last = fruits[fruits.length - 1];
// Banana
```

### 迭代陣列

```js
fruits.forEach(function (item, index, array) {
  console.log(item, index);
});
// Apple 0
// Banana 1
```

### 加入項目至陣列末端

```js
var newLength = fruits.push("Orange");
// ["Apple", "Banana", "Orange"]
```

### 移除陣列末端項目

```js
var last = fruits.pop(); // 移除（最末端的）Orange
// ["Apple", "Banana"];
```

### 移除陣列前端項目

```js
var first = fruits.shift(); // 移除（最前端的）Apple
// ["Banana"];
```

### 加入項目至陣列前端

```js
var newLength = fruits.unshift("Strawberry"); // 加到陣列前端
// ["Strawberry", "Banana"];
```

### 在陣列中尋找項目的索引

```js
fruits.push("Mango");
// ["Strawberry", "Banana", "Mango"]

var pos = fruits.indexOf("Banana");
// 1
```

### 移除指定索引位置的項目

```js
var removedItem = fruits.splice(pos, 1); // 移除 pos 起的 1 個項目

// ["Strawberry", "Mango"]
```

### 移除指定索引位置起的多個項目

```js
var vegetables = ["Cabbage", "Turnip", "Radish", "Carrot"];
console.log(vegetables);
// ["Cabbage", "Turnip", "Radish", "Carrot"]

var pos = 1,
  n = 2;

var removedItems = vegetables.splice(pos, n);
// 這就是移除項目的方式，
// n 表示從該位置開始，一直到陣列的尾端有多少項目需要移除

console.log(vegetables);
// ["Cabbage", "Carrot"]（原始的陣列被改變）

console.log(removedItems);
// ["Turnip", "Radish"]
```

### 複製陣列

```js
var shallowCopy = fruits.slice(); // 這就是複製陣列的方式
// ["Strawberry", "Mango"]
```

### 建立二維陣列

以下範例會用字串產生一張西洋棋盤的二維陣列。第一步是將士兵 'p' 從 (6,4) 移動至 (4,4)，然後清空原本的位置 (6,4)。

```js
var board = [
  ["R", "N", "B", "Q", "K", "B", "N", "R"],
  ["P", "P", "P", "P", "P", "P", "P", "P"],
  [" ", " ", " ", " ", " ", " ", " ", " "],
  [" ", " ", " ", " ", " ", " ", " ", " "],
  [" ", " ", " ", " ", " ", " ", " ", " "],
  [" ", " ", " ", " ", " ", " ", " ", " "],
  ["p", "p", "p", "p", "p", "p", "p", "p"],
  ["r", "n", "b", "q", "k", "b", "n", "r"],
];

console.log(board.join("\n") + "\n\n");

// 將士兵往前移兩步
board[4][4] = board[6][4];
board[6][4] = " ";
console.log(board.join("\n"));
```

以下是輸出結果：
```

R,N,B,Q,K,B,N,R
P,P,P,P,P,P,P,P
 , , , , , , ,
 , , , , , , ,
 , , , , , , ,
 , , , , , , ,
p,p,p,p,p,p,p,p
r,n,b,q,k,b,n,r

R,N,B,Q,K,B,N,R
P,P,P,P,P,P,P,P
 , , , , , , ,
 , , , , , , ,
 , , , ,p, , ,
 , , , , , , ,
p,p,p,p, ,p,p,p
r,n,b,q,k,b,n,r
```

### 使用陣列來以表格顯示多個數值

```js
values = [];
for (var x = 0; x < 10; x++) {
  values.push([2 \*\* x, 2 \* x \*\* 2]);
}
console.table(values);
```

結果會是
```

0 1   0
1 2   2
2 4   8
3 8   18
4 16  32
5 32  50
6 64  72
7 128 98
8 256 128
9 512 162
```

（第一欄為索引）

規範
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-array-objects |

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

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

參見
--

* JavaScript Guide: 「Indexing object properties」 (en-US)
* JavaScript Guide: 「Predefined Core Objects: `Array` Object」 (en-US)
* Array comprehensions (en-US)
* Polyfill for JavaScript 1.8.5 Array Generics and ECMAScript 5 Array Extras
* Typed Arrays


ArrayBuffer - JavaScript

ArrayBuffer
===========

**`ArrayBuffer`** 物件是一種表示通用、固定大小的原始二進制資料緩衝。想要直接操作一個 `ArrayBuffer` 物件的內容是不可能的。若要讀寫該緩衝的內容則必須透過視圖，可以選擇建立一個 `DataView` (en-US) 視圖物件或是一個限定其成員為某種型別的 `TypedArray` (en-US) 視圖物件，它們皆能以特定的型別解讀、修改 `ArrayBuffer`。

語法
--

```js
new ArrayBuffer(length)
```

### 參數

`length`

要建立的緩衝陣列大小，以位元組（byte）計算。

### 回傳值

為一個新建立的指定大小 `ArrayBuffer` 物件，其內容皆初始化為 0。

### Exceptions

A `RangeError` is thrown if the `length` is larger than `Number.MAX_SAFE_INTEGER` (en-US) (>= 2 \*\* 53) or negative.

說明
--

The `ArrayBuffer` constructor creates a new `ArrayBuffer` of the given length in bytes.

### 從既有的資料取得 ArrayBuffer

* From a Base64 string (en-US)
* 從本地端檔案
屬性
--

`ArrayBuffer.length`

The `ArrayBuffer` constructor's length property whose value is 1.

`get ArrayBuffer[@@species]` (en-US)

The constructor function that is used to create derived objects.

`ArrayBuffer.prototype` (en-US)

Allows the addition of properties to all `ArrayBuffer` objects.

方法
--

`ArrayBuffer.isView(arg)` (en-US)

Returns `true` if `arg` is one of the ArrayBuffer views, such as typed array objects (en-US) or a `DataView` (en-US). Returns `false` otherwise.

`ArrayBuffer.transfer(oldBuffer [, newByteLength])` (en-US)
實驗性質

Returns a new `ArrayBuffer` whose contents are taken from the `oldBuffer`'s data and then is either truncated or zero-extended by `newByteLength`.

`ArrayBuffer` 實例
----------------

所有的 `ArrayBuffer` 物件實例皆繼承自 `ArrayBuffer.prototype` (en-US).

### 屬性

`ArrayBuffer.prototype[@@toStringTag]`

The initial value of the `@@toStringTag` (en-US) property is the string `"ArrayBuffer"`. This property is used in `Object.prototype.toString()` (en-US).

`ArrayBuffer.prototype.byteLength` (en-US)

The read-only size, in bytes, of the `ArrayBuffer`. This is established when the array is constructed and cannot be changed.

### 方法

`ArrayBuffer.prototype.slice()` (en-US)

Returns a new `ArrayBuffer` whose contents are a copy of this `ArrayBuffer`'s bytes from `begin` (inclusive) up to `end` (exclusive). If either `begin` or `end` is negative, it refers to an index from the end of the array, as opposed to from the beginning.

範例
--

In this example, we create a 8-byte buffer with a `Int32Array` (en-US) view referring to the buffer:

```js
var buffer = new ArrayBuffer(8);
var view = new Int32Array(buffer);
```

規範
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-arraybuffer-objects |

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

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

相容性備註
-----

Starting with ECMAScript 2015, `ArrayBuffer` constructors require to be constructed with a `new` operator. Calling an `ArrayBuffer` constructor as a function without `new`, will throw a `TypeError` (en-US) from now on.

```js
var dv = ArrayBuffer(10);
// TypeError: calling a builtin ArrayBuffer constructor
// without new is forbidden
```

```js
var dv = new ArrayBuffer(10);
```

參見
--

* `TypedArray` (en-US)
* `SharedArrayBuffer` (en-US)


JSON - JavaScript

JSON
====

**`JSON`** 物件包含了解析、或是轉換為 JavaScript Object Notation（JSON）格式的方法。這物件不能被呼叫或建構；而除了它的兩個方法屬性以外，本身也沒有特別的功能。

描述
--

### JavaScript Object Notation

JSON 是序列物件、陣列、數字、字串、布林值、還有 `null` (en-US) 的語法。它建基、但不同於 JavaScript：有些 JavaScript 不是 JSON、而有些 JSON 不是 JavaScript。請參見 JSON: The JavaScript subset that isn't。

| JavaScript 型別 | 與 JSON 的差別 |
| --- | --- |
| 物件與陣列 | 屬性名稱必須是包含在雙引號中的字串；禁止尾後逗號。 |
| 數字 | 數字不可以 0 作為開頭(在 JSON.stringify 0 會被忽略，但是在 JSON.parse 會造成語法錯誤)；小數點前面必須至少有一位數字。 |
| 字串 | Only a limited set of characters may be escaped; certain control characters are prohibited; the Unicode line separator (U+2028) and paragraph separator (U+2029) characters are permitted; strings must be double-quoted. See the following example where `JSON.parse()` works fine and a `SyntaxError` (en-US) is thrown when evaluating the code as JavaScript: |

```js
const code = '"\u2028\u2029"';
JSON.parse(code); // evaluates to "\u2028\u2029" in all engines
eval(code); // throws a SyntaxError in old engines
```

JSON 的完整語法如下：
```

JSON = null
    or true or false
    or JSONNumber
    or JSONString
    or JSONObject
    or JSONArray

JSONNumber = - PositiveNumber
          or PositiveNumber
PositiveNumber = DecimalNumber
              or DecimalNumber . Digits
              or DecimalNumber . Digits ExponentPart
              or DecimalNumber ExponentPart
DecimalNumber = 0
             or OneToNine Digits
ExponentPart = e Exponent
            or E Exponent
Exponent = Digits
        or + Digits
        or - Digits
Digits = Digit
      or Digits Digit
Digit = 0 through 9
OneToNine = 1 through 9

JSONString = ""
          or " StringCharacters "
StringCharacters = StringCharacter
                or StringCharacters StringCharacter
StringCharacter = any character
                  except " or \ or U+0000 through U+001F
               or EscapeSequence
EscapeSequence = \" or \/ or \\ or \b or \f or \n or \r or \t
              or \u HexDigit HexDigit HexDigit HexDigit
HexDigit = 0 through 9
        or A through F
        or a through f

JSONObject = { }
          or { Members }
Members = JSONString : JSON
       or Members , JSONString : JSON

JSONArray = [ ]
         or [ ArrayElements ]
ArrayElements = JSON
             or ArrayElements , JSON
```

Insignificant whitespace may be present anywhere except within a `JSONNumber` (numbers must contain no whitespace) or `JSONString` (where it is interpreted as the corresponding character in the string, or would cause an error). The tab character (U+0009), carriage return (U+000D), line feed (U+000A), and space (U+0020) characters are the only valid whitespace characters.

方法
--

`JSON.parse()`

解析 JSON 字串，能改變給定值和屬性、接著回傳解析值。

`JSON.stringify()` (en-US)

回傳給定的 JSON 對應字串，可自行決定只想包括哪些特定屬性、或替換的屬性值。

Polyfill
--------

舊版瀏覽器並不支援 `JSON`。你可以在程式碼開頭插入以下程式碼，以解決這個問題。這個程式碼能實做不支援原生 `JSON` 物件的瀏覽器（如 Internet Explorer 6）。

以下演算法能仿製原生 `JSON` 物件。

```js
if (!window.JSON) {
  window.JSON = {
    parse: function (sJSON) {
      return eval("(" + sJSON + ")");
    },
    stringify: (function () {
      var toString = Object.prototype.toString;
      var hasOwnProperty = Object.prototype.hasOwnProperty;
      var isArray =
        Array.isArray ||
        function (a) {
          return toString.call(a) === "[object Array]";
        };
      var escMap = {
        '"': '\\"',
        "\\": "\\\\",
        "\b": "\\b",
        "\f": "\\f",
        "\n": "\\n",
        "\r": "\\r",
        "\t": "\\t",
      };
      var escFunc = function (m) {
        return (
          escMap[m] ||
          "\\u" + (m.charCodeAt(0) + 0x10000).toString(16).substr(1)
        );
      };
      var escRE = /[\\"\u0000-\u001F\u2028\u2029]/g;
      return function stringify(value) {
        if (value == null) {
          return "null";
        } else if (typeof value === "number") {
          return isFinite(value) ? value.toString() : "null";
        } else if (typeof value === "boolean") {
          return value.toString();
        } else if (typeof value === "object") {
          if (typeof value.toJSON === "function") {
            return stringify(value.toJSON());
          } else if (isArray(value)) {
            var res = "[";
            for (var i = 0; i < value.length; i++)
              res += (i ? ", " : "") + stringify(value[i]);
            return res + "]";
          } else if (toString.call(value) === "[object Object]") {
            var tmp = [];
            for (var k in value) {
              // in case "hasOwnProperty" has been shadowed
              if (hasOwnProperty.call(value, k))
                tmp.push(stringify(k) + ": " + stringify(value[k]));
            }
            return "{" + tmp.join(", ") + "}";
          }
        }
        return '"' + value.toString().replace(escRE, escFunc) + '"';
      };
    })(),
  };
}
```

More complex well-known polyfills for the `JSON` object are JSON2 and JSON3.

規範
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-json-object |

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

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

參見
--

* `Date.prototype.toJSON()` (en-US)


isNaN() - JavaScript

isNaN()
=======

**`isNaN()`** 函式會判斷某個數值是不是 `NaN`。注意：在 `isNaN` 函式裡面，有個有趣的強制性規則。你可能會想改用在 ECMAScript 2015 導入的 `Number.isNaN()` (en-US)。

嘗試一下
----

語法
--

```js
isNaN(value)
```

### 參數

`value`

要測試的數值。

### 回傳值

如果給定值是 `NaN` 就回傳 **`true`**、否則就回傳 **`false`**。

描述
--

### 為什麼要用 `isNaN` 函式

與其他 JavaScript 的值不同，你不可能靠等號運算符（== 與 ===）來判斷某個值是不是 `NaN`，因為連 `NaN == NaN` 與 `NaN === NaN` 的結果都是 `false`。因此，`isNaN` 函式是必要的。

### `NaN` 值的來源

`NaN` 會在算術運算（arithmetic operations）出現 *undefined* 或是 *unrepresentable* 值的結果時產生。這些值不一定是溢出條件。`NaN` 亦為試圖給毫無可用數字的原始值、予以強制運算之結果。

例如，零除以零的結果會是 `NaN`——不過把其他數字除以零則不是 `NaN`。

### 令人困惑的特殊狀況行為

從最早的 `isNaN` 函式版本規範始，其針對非數值之行為，不斷教人困惑至極。當 `isNaN` 函式的參數並非數字型別時，此值會先強制轉換到數字。該值接著會測定此值是否為 `NaN`。因此，當被強制轉換的非數字，給出了有效的非 NaN 值（經典案例為空的字串與布林原始值：它們在強制轉換時，會給予數字結果 0 或 1）時，會回傳不如預期的「false」值：以空的字串為例，它很明顯地「非數字」。這段教人糾結的點，乃出於「非數字」術語的「數字」一詞、由 IEEE-754 浮點值定義之事實而來。這個函式要解釋為「當這個值，被強制轉換為數值時，它還是 IEEE-754 的『非數字』值嗎？」的答案。

最新的 ECMAScript（ES2015）版本導入了 `Number.isNaN()` (en-US) 函式。儘管 `Number.isNaN` 的 `NaN` 依舊維持了數字上的意義、而不是簡單的「非數字」，`Number.isNaN(x)` 在偵測 `x` 為 `NaN` 與否時比較可靠。另外，如果在缺少 `Number.isNaN` 的情況下，通過表達式`(x != x)` 來檢測變量`x`是否是 NaN 會更加的可靠。

一個 `isNaN` 的 polyfill 可以理解為（這個 polyfill 利用了 `NaN` 自身永不等於自身這一特性）：

```js
var isNaN = function (value) {
  var n = Number(value);
  return n !== n;
};
```

範例
--

```js
isNaN(NaN); // true
isNaN(undefined); // true
isNaN({}); // true

isNaN(true); // false
isNaN(null); // false
isNaN(37); // false

// 字串
isNaN("37"); // false: "37" 轉換成數字的 37 後就不是 NaN 了
isNaN("37.37"); // false: "37.37" 轉換成數字的 37.37 後就不是 NaN 了
isNaN("123ABC"); // true: parseInt("123ABC") 是 123 但 Number("123ABC") 是 NaN
isNaN(""); // false: 空字串轉換成數字的 0 後就不是 NaN 了
isNaN(" "); // false: 有空白的字串轉換成數字的 0 後就不是 NaN 了

// 日期
isNaN(new Date()); // false
isNaN(new Date().toString()); // true

// 這個偵測的錯誤是不能完全信賴 isNaN 的理由
isNaN("blabla"); // true: "blabla" 被轉換為數字，將其解析為數字失敗後回傳了 NaN
```

### 實用的特殊狀況行為

當然，你能以更用途導向的方法去思考 `isNaN()`：如果 `isNaN()` 回傳 `false`，那麼把 `x` 用在任何算術表達式都不會回傳 `NaN`。相反地，如果回傳 `true`，那麼把 `x` 用在任何算術表達式都會是 `NaN`。這在 JavaScript 的意義是 `isNaN(x) == true` 等於 `x - 0` 回傳 `NaN`（儘管在 JavaScript 裡面 `x - 0 == NaN` 永遠回傳 false，你因而無法測試）── 事實上，`isNaN(x)`、`isNaN(x - 0)`、`isNaN(Number(x))`、`Number.isNaN(x - 0)`、`Number.isNaN(Number(x))` 在 JavaScript 裡面，都會回傳一樣的東西。而 `isNaN(x)` 是所有表達式裡面最短的一種。

比方說，你可以用這個式子，去測試函式的參數能不能透過算術處理（也就是能「像」數字一樣被利用）、否則就提供預設值之類的。你可以透過上下文的根據以隱式數值轉換（implicitly converting value），以使用 JavaScript 提供的全部功能。

```js
function increment(x) {
  if (isNaN(x)) x = 0;
  return x + 1;
}

// 與 Number.isNaN() 一樣：
function increment(x) {
  if (Number.isNaN(Number(x))) x = 0;
  return x + 1;
}

// 以下範例的函式參數 x，isNaN(x) 都會回傳 false，
// 儘管 x 不是數字，依舊能用在算術表達式。
increment(""); // 1: "" 被轉換成 0
increment(new String()); // 1: 空字串的新字串物件被轉換成 0
increment([]); // 1: [] 被轉換成 0
increment(new Array()); // 1: 空陣列的新陣列物件被轉換成 0
increment("0"); // 1: "0" 被轉換成 0
increment("1"); // 2: "1" 被轉換成 1
increment("0.1"); // 1.1: "0.1" 被轉換成 0.1
increment("Infinity"); // Infinity: "Infinity" 被轉換成 Infinity
increment(null); // 1: null 被轉換成 0
increment(false); // 1: false 被轉換成 0
increment(true); // 2: true 被轉換成 1
increment(new Date()); // 回傳以毫秒為單位加 1，當今的日期/時間

// 以下範例的函式參數 x，isNaN(x) 都會回傳 false，而 x 的確是數字。
increment(-1); // 0
increment(-0.1); // 0.9
increment(0); // 1
increment(1); // 2
increment(2); // 3
// …等等…
increment(Infinity); // Infinity

// 以下範例的函式參數 x，isNaN(x) 都會回傳 true，x 也的確不是數字。
// 使得函式會被 0 取代，並回傳 1
increment(String); // 1
increment(Array); // 1
increment("blabla"); // 1
increment("-blabla"); // 1
increment(0 / 0); // 1
increment("0/0"); // 1
increment(Infinity / Infinity); // 1
increment(NaN); // 1
increment(undefined); // 1
increment(); // 1

// isNaN(x) 與 isNaN(Number(x)) 永遠一樣，不過這裡的 x 是強制存在的！
isNaN(x) == isNaN(Number(x)); // 針對所有 x 的值都是 true，x == undefined 也不例外，
// 因為 isNaN(undefined) == true 且 Number(undefined) 回傳 NaN，
// 不過……
isNaN() == isNaN(Number()); // false，因為 isNaN() == true 且 Number() == 0
```

規範
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-isnan-number |

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

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

參見
--

* `NaN`
* `Number.isNaN()` (en-US)


Boolean - JavaScript

Boolean
=======

**`Boolean`** 是布林值的包覆器。

語法
--
```

new Boolean([value])
```

### 參數

`value` 選擇性

這個`Boolean`物件的初始值。

說明
--

傳入的第一個參數值，如果需要的話，會被轉換成布林值。如果沒傳值，或者是`0`、`-0`、`null` (en-US)、`false`、`NaN`、`undefined` (en-US)、空字串(`""`)的話，這個物件的值會被初始化成`false`。大多數情況下，DOM 物件 `document.all` (en-US) 被傳入後，也會將其初始化為`false`。至於其他的值，包含所有物件或`"false"`字串，都會使其初始化為`true`。

不要將原始型別的布林值和這個布林物件搞混，它們並不相同。

在判斷式中，任何物件只要不是 `undefined` (en-US) 或 `null` (en-US) ，儘管是值為`false` 的 `Boolean` 物件，都會被轉換成`true`。舉例來說，下列的 `if` 判斷式中的布林值即為`true`：

```js
var x = new Boolean(false);
if (x) {
  // this code is executed
}
```

以上這個行為和`Boolean`原始型別沒有關連，反倒是下面的 `if` 判斷式會正確地將其視為`false`：

```js
var x = false;
if (x) {
  // this code is not executed
}
```

不要用`Boolean`物件將非布林值轉換成布林值。反而要將`Boolean`視為函式去轉換非布林值：

```js
var x = Boolean(expression); // 較好
var x = new Boolean(expression); // 不要用
```

如果你要指定任何物件，包括值為`false`的`Boolean`物件，作為`Boolean`物件的初始值，則該`Boolean`物件的值依舊為`true`。

```js
var myFalse = new Boolean(false); // 初始值給false，實際上為true
var g = new Boolean(myFalse); // 想當然耳，true
var myString = new String("Hello"); // 字串物件，'Hello'
var s = new Boolean(myString); // 依舊為true
```

不要使用`Boolean`物件代替`Boolean`的原始型別！

屬性
--

`Boolean.length`

長度永遠為 1。

`Boolean.prototype` (en-US)

原型為`Boolean`的建構式。

方法
--

全域的`Boolean`物件自身沒有任何方法，它只有從原型鏈繼承而來的方法。

`Boolean` 實體
------------

所有 `Boolean` 實體會繼承 `Boolean.prototype` (en-US) 。和所有建構式一樣，原型物件會指派給實體那些繼承的屬性和方法。

### 方法

`Boolean.prototype.toString()` (en-US)

Returns a string of either `true` or `false` depending upon the value of the object. Overrides the `Object.prototype.toString()` (en-US) method.

`Boolean.prototype.valueOf()` (en-US)

Returns the primitive value of the `Boolean` object. Overrides the `Object.prototype.valueOf()` (en-US) method.

範例
--

### 用 `false` 作為初始值建立 `Boolean` 物件

```js
var bNoParam = new Boolean();
var bZero = new Boolean(0);
var bNull = new Boolean(null);
var bEmptyString = new Boolean("");
var bfalse = new Boolean(false);
```

### 用 `true` 作為初始值建立 `Boolean` 物件

```js
var btrue = new Boolean(true);
var btrueString = new Boolean("true");
var bfalseString = new Boolean("false");
var bSuLin = new Boolean("Su Lin");
var bArrayProto = new Boolean([]);
var bObjProto = new Boolean({});
```

規範
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-boolean-objects |

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

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

參見
--

* `Boolean.prototype` (en-US)
* Boolean
* Boolean data type (Wikipedia)


Set.prototype.clear() - JavaScript

Set.prototype.clear()
=====================

**`clear()`** 方法會從一個 `Set` 物件中移除其所有元素。

嘗試一下
----

語法
--

```js
mySet.clear();
```

### 回傳值

`undefined` (en-US)。

範例
--

### 使用 `clear` 方法

```js
var mySet = new Set();
mySet.add(1);
mySet.add("foo");

mySet.size; // 2
mySet.has("foo"); // true

mySet.clear();

mySet.size; // 0
mySet.has("bar"); // false
```

規範
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-set.prototype.clear |

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

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

參見
--

* `Set`
* `Set.prototype.delete()`


Set.prototype.values() - JavaScript

Set.prototype.values()
======================

**`values()`** 方法回傳一個 `Iterator` 物件，包含著 `Set` 物件中所有元素，由插入順序排序。

**`keys()`** 是這個方法的替身（為了與 `Map` 物件保持相似性）；他運行的完全一模一樣，回傳 `Set` 中元素的 **values**。

嘗試一下
----

語法
--

```js
mySet.values();
```

### 回傳值

一個 `Iterator` 物件，包含著 `Set` 物件中所有元素，由插入順序排序。

範例
--

### 使用 `values()`

```js
var mySet = new Set();
mySet.add("foo");
mySet.add("bar");
mySet.add("baz");

var setIter = mySet.values();

console.log(setIter.next().value); // "foo"
console.log(setIter.next().value); // "bar"
console.log(setIter.next().value); // "baz"
```

規範
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-set.prototype.values |

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

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

參見
--

* `Set.prototype.entries()`


Set.prototype.has() - JavaScript

Set.prototype.has()
===================

**`has()`** 方法對一個指定值元素在 `Set` 物件中的存在與否回傳一個布林值。

嘗試一下
----

語法
--

```js
mySet.has(value);
```

### 參數

`value`

要測試是否存在在 `Set` 中的值。

### 回傳值

回傳 `true` 如果給定值存在在 `Set` 物件中；反之回傳 `false`。

**備註：** 技術上來說，`has()` 使用了 `sameValueZero` 算法來判斷給定元素的存在與否。

範例
--

### 使用 `has` 方法

```js
var mySet = new Set();
mySet.add("foo");

mySet.has("foo"); // returns true
mySet.has("bar"); // returns false

var set1 = new Set();
var obj1 = { key1: 1 };
set1.add(obj1);

set1.has(obj1); // returns true
set1.has({ key1: 1 }); // returns false because they are different object references
set1.add({ key1: 1 }); // now set1 contains 2 entries
```

規範
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-set.prototype.has |

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

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

參見
--

* `Set`
* `Set.prototype.add()`
* `Set.prototype.delete()`


Set.prototype.entries() - JavaScript

Set.prototype.entries()
=======================

**`entries()`** 方法回傳一個 `Iterator` 物件，其包含著一個由插入順序排序，`Set` 物件中每個元素的 **`[value, value]`** 陣列。儘管對 `Set` 物件來說沒有像 `Map` 一樣的 `key` 概念，為了確保這個 API 運作的與 `Map` 相似，每個 *entry* 都有同樣的值同時作為其 *key* 和 *value* ，因此回傳的是一個 **`[value, value]`** 的陣列。

嘗試一下
----

語法
--

```js
mySet.entries()
```

### 回傳值

一個新的 `Iterator` 物件，包含著一個由插入順序排序，`Set` 物件中每個元素的 **`[value, value]`** 陣列。

範例
--

### 使用 `entries()`

```js
var mySet = new Set();
mySet.add("foobar");
mySet.add(1);
mySet.add("baz");

var setIter = mySet.entries();

console.log(setIter.next().value); // ["foobar", "foobar"]
console.log(setIter.next().value); // [1, 1]
console.log(setIter.next().value); // ["baz", "baz"]
```

規範
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-set.prototype.entries |

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

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

參見
--

* `Set.prototype.keys()` (en-US)
* `Set.prototype.values()`


Set.prototype.add() - JavaScript

Set.prototype.add()
===================

**`add()`** 會在一個 `Set` 物件的尾端加上一個指定 `value` 的新元素。

嘗試一下
----

語法
--

```js
mySet.add(value);
```

### 參數

`value`

要被加到 `Set` 物件中的值。

### 回傳值

`Set` 物件本身。

範例
--

### 使用 `add` 方法

```js
var mySet = new Set();

mySet.add(1);
mySet.add(5).add("some text"); // chainable

console.log(mySet);
// Set [1, 5, "some text"]
```

規範
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-set.prototype.add |

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

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

參見
--

* `Set`
* `Set.prototype.delete()`
* `Set.prototype.has()`


Set.prototype.delete() - JavaScript

Set.prototype.delete()
======================

**`delete()`** 方法會一個 `Set` 物件中移除指定元素。

嘗試一下
----

語法
--

```js
mySet.delete(value);
```

### 參數'

`value`

要從 `Set` 物件中移除的值。

### 回傳值

`true` 如果成功從 `Set` 物件中移除；反之 `false`。

範例
--

### 使用 `delete` 方法

```js
var mySet = new Set();
mySet.add("foo");

mySet.delete("bar"); // Returns false. No "bar" element found to be deleted.
mySet.delete("foo"); // Returns true. Successfully removed.

mySet.has("foo"); // Returns false. The "foo" element is no longer present.
```

下方展示了如何從一個 Set 中移除物件。

```js
var setObj = new Set(); // Create a New Set.

setObj.add({ x: 10, y: 20 }); // Add object in the set.

setObj.add({ x: 20, y: 30 }); // Add object in the set.

// Delete any point with `x > 10`.
setObj.forEach(function (point) {
  if (point.x > 10) {
    setObj.delete(point);
  }
});
```

規範
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-set.prototype.delete |

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

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

參見
--

* `Set`
* `Set.prototype.clear()`


Object.keys() - JavaScript

Object.keys()
=============

**`Object.keys()`** 方法會回傳一個由指定物件所有可列舉之屬性組成的陣列，該陣列中的的排列順序與使用 `for...in` 進行迭代的順序相同（兩者的差異在於 `for-in` 迴圈還會迭代出物件自其原型鏈所繼承來的可列舉屬性）。

語法
--

```js
Object.keys(obj)
```

### 參數

`obj`

物件，用以回傳其可列舉屬性。

### 回傳值

回傳一個包含給定物件內所有可列舉屬性的字串陣列。

描述
--

`Object.keys()` 回傳一個陣列，陣列中的各元素為直屬於 `obj` ，對應可列舉屬性名的字串。回傳結果的排序，與手動對物件屬性作迴圈迭代的結果排序相同。

範例
--

```js
var arr = ["a", "b", "c"];
console.log(Object.keys(arr)); // console: ['0', '1', '2']

// 類似陣列的物件
var obj = { 0: "a", 1: "b", 2: "c" };
console.log(Object.keys(obj)); // console: ['0', '1', '2']

// 擁有隨機 key 排序，類似陣列的物件
var an_obj = { 100: "a", 2: "b", 7: "c" };
console.log(Object.keys(an_obj)); // console: ['2', '7', '100']

// getFoo 不是可列舉的屬性
var my_obj = Object.create(
  {},
  {
    getFoo: {
      value: function () {
        return this.foo;
      },
    },
  },
);
my_obj.foo = 1;

console.log(Object.keys(my_obj)); // console: ['foo']
```

如果想取得物件的所有屬性，包括非可列舉的屬性，請參閱 `Object.getOwnPropertyNames()` (en-US).

備註
--

在 ES5 中，如果這個方法的參數不是一個標準物件(例如原始型別)，將會產生 `TypeError` (en-US)錯誤。而在 ES2015，非物件的參數將會強制轉換成物件。

```js
Object.keys("foo");
// TypeError: "foo" is not an object (ES5 code)

Object.keys("foo");
// ["0", "1", "2"] (ES2015 code)
```

Polyfill
--------

如需在原生不支援、較舊的環境中增加 `Object.keys` 的相容性，請複製以下片段：

```js
// From https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global\_Objects/Object/keys
if (!Object.keys) {
  Object.keys = (function () {
    "use strict";
    var hasOwnProperty = Object.prototype.hasOwnProperty,
      hasDontEnumBug = !{ toString: null }.propertyIsEnumerable("toString"),
      dontEnums = [
        "toString",
        "toLocaleString",
        "valueOf",
        "hasOwnProperty",
        "isPrototypeOf",
        "propertyIsEnumerable",
        "constructor",
      ],
      dontEnumsLength = dontEnums.length;

    return function (obj) {
      if (
        typeof obj !== "object" &&
        (typeof obj !== "function" || obj === null)
      ) {
        throw new TypeError("Object.keys called on non-object");
      }

      var result = [],
        prop,
        i;

      for (prop in obj) {
        if (hasOwnProperty.call(obj, prop)) {
          result.push(prop);
        }
      }

      if (hasDontEnumBug) {
        for (i = 0; i < dontEnumsLength; i++) {
          if (hasOwnProperty.call(obj, dontEnums[i])) {
            result.push(dontEnums[i]);
          }
        }
      }
      return result;
    };
  })();
}
```

請注意以上的代碼片段在 IE7 中( IE8 也有可能 )，從不同的 window 傳入物件將包含非可列舉的 key 。

較精簡的瀏覽器 Polyfill，請參閱 Javascript - Object.keys Browser Compatibility.

規範
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-object.keys |

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

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

參見
--

* Enumerability and ownership of properties (en-US)
* `Object.prototype.propertyIsEnumerable()` (en-US)
* `Object.create()`
* `Object.getOwnPropertyNames()` (en-US)
* `Object.values()` (en-US)
實驗性質
* `Object.entries()` (en-US)
實驗性質


Object.freeze() - JavaScript

Object.freeze()
===============

**`Object.freeze()`** 顧名思義是用來「凍結」一個物件的：也就是防止物件新增屬性；防止物件既有的屬性被刪除；防止物件原本的屬性，還有屬性的可列舉性，可設定性，可寫性被改動；同時它也防止物件的原型被改變。此方法回傳一個凍結狀態的物件。

嘗試一下
----

語法
--

```js
Object.freeze(obj)
```

### 參數

`obj`

要被凍結的物件

### 回傳值

被凍結的物件

描述
--

一個被凍結的物件無法被新增或刪除屬性。任何想要改動的嘗試都會失敗，要不沈默地失敗，就是丟出一個 `TypeError` (en-US) 例外（此例外最常出現在 嚴格模式）

資料屬性無法被改變。訪問者方法——setter 也一樣不能改變資料屬性（雖然它會給你可以改變資料值的假象）。注意！ 如果資料屬性是物件的話，該物件的值是可以被改變的，除非它們也被凍結。一個陣列同樣可以被凍結（因為它也是一個物件），被凍結後它的元素內容就不能被改變，也不能新增或刪除元素。

範例
--

### 凍結物件

```js
var obj = {
  prop: function () {},
  foo: "bar",
};

// 新的屬性可以被新增，原本的屬性可以被改變或刪除
obj.foo = "baz";
obj.lumpy = "woof";
delete obj.prop;

// 回傳的物件跟原本傳入的物件是同一個，所以不需要記住回傳值
// 也可以凍結一個物件
var o = Object.freeze(obj);

o === obj; // true
Object.isFrozen(obj); // === true

// 現在任何改動都會失敗
obj.foo = "quux"; // 什麼事也不會發生
// 屬性無法被新增
obj.quaxxor = "the friendly duck";

// 在嚴格模式中，如方才的嘗試都會丟出 TypeError
function fail() {
  "use strict";
  obj.foo = "sparky"; // 丟出 TypeError
  delete obj.foo; // 丟出 TypeError
  delete obj.quaxxor; // 回傳 true 因為屬性 'quaxxor' 從來沒有被新增
  obj.sparky = "arf"; // 丟出 TypeError
}

fail();

// 嘗試透過 Object.defineProperty 來改變屬性的值會丟出 TypeError
Object.defineProperty(obj, "ohai", { value: 17 });
Object.defineProperty(obj, "foo", { value: "eit" });

// 一樣不可能改變物件的原型，都會丟出 TypeError
Object.setPrototypeOf(obj, { x: 20 });
obj.__proto__ = { x: 20 };
```

### 凍結陣列

```js
let a = [0];
Object.freeze(a); // 現在這個陣列不能被改動

a[0] = 1; // 沈默地失敗
a.push(2); // 沈默地失敗

// 在嚴格模式底下會丟出 TypeError
function fail() {
  "use strict";
  a[0] = 1;
  a.push(2);
}

fail();
```

被凍結的物件是*不可變*（Immutable）的。然而，它並不等於*常數*（constant）。以下的範例顯示一個被凍結的物件並不是常數（凍結的動作是「淺」的——如果資料屬性也是物件，不會遞迴地做凍結的動作）。

```js
obj1 = {
  internal: {},
};

Object.freeze(obj1);
obj1.internal.a = "aValue";

obj1.internal.a; // 'aValue'
```

如果要成為一個常數物件，整個物件參考圖（包含直接指向或間接指向其他物件）必須都只能指向被凍結的不可變物件。一個物件被稱作不可變是因為它的整個物件狀態值（或是指向其他物件的參考）是固定的。注意，string、number 和 boolean 這些原始型別的值是永遠不變的，Function 和 Array 都屬於物件的一種。

要讓一個物件變成常數物件，就必須遞迴地凍結每個是物件型別的屬性 (稱作深凍結)。只有當你知道物件的參考圖不存在任何*循環*的時候才能使用以上遞迴的方式來凍結物件，不然就可能會造成無窮迴圈。一個改善以下範例中 deepFreeze() 來解決無窮迴圈問題的方法是——創建一個接受一個路徑參數 (像是陣列) 的內部用函數，用來避免無窮遞迴地呼叫 deepFreeze()——當發現欲凍結的物件已經出現在之前凍結的物件行列中就不繼續遞迴下去。需要注意的是你可能會不小心凍結一個不應該被凍結的物件，像是 [window]。

```js
// 用這個函數來進行對物件的深凍結
function deepFreeze(obj) {
  // 取得物件的所有屬性名稱
  var propNames = Object.getOwnPropertyNames(obj);

  // 在凍結物件本身之前先凍結物件的所有物件屬性
  propNames.forEach(function (name) {
    var prop = obj[name];

    // 凍結 prop 如果它是一個物件
    if (typeof prop == "object" && prop !== null) deepFreeze(prop);
  });

  // 凍結本身 (no-op 如果已經被凍結了)
  return Object.freeze(obj);
}

obj2 = {
  internal: {},
};

deepFreeze(obj2);
obj2.internal.a = "anotherValue";
obj2.internal.a; // undefined
```

備註
--

在 ES5 中，如果傳入此方法的參數不是一個物件（原始型別），`TypeError` (en-US) 就會被丟出。而在 ES2015，一個非物件型態的參數會被當成是一個已經被凍結的物件，所以會被直接回傳，不會造成錯誤。

```js
> Object.freeze(1)
TypeError: 1 is not an object // ES5 code

> Object.freeze(1)
1                             // ES2015 code
```

### 與 `Object.seal()` 的差別

被 `Object.seal()` 密封的物件還是可以改變它原有屬性的值。而被 `Object.freeze()` 凍結的物件則無法改變它原有屬性的值因為他們是不可變的。

規格
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-object.freeze |

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

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

參閱
--

* `Object.isFrozen()` (en-US)
* `Object.preventExtensions()`
* `Object.isExtensible()` (en-US)
* `Object.seal()` (en-US)
* `Object.isSealed()` (en-US)


Object.create() - JavaScript

Object.create()
===============

**`Object.create()`** 指定其原型物件與屬性，創建一個新物件。

語法
--

```js
Object.create(proto[, propertiesObject])
```

### 參數

`proto`

指定新物件的原型 (prototype) 物件。

`propertiesObject`

選用，為一物件。如有指定且非 `undefined` (en-US)，則此參數物件中可列舉出的屬性 (即參數物件自身定義的屬性，並非指原型鏈上的 `enumerable` 特性 ) 對應其屬性名稱，根據其屬性敘述元 (property descriptors) 加進新創建的物件。這些屬性對應到 `Object.defineProperties()` (en-US) 的第二個參數。

### 回傳

具有指定原型物件與屬性的新物件。

### 例外

如果 `proto` 參數不是 `null` (en-US) 或一個物件，將會拋出 `TypeError` (en-US) 例外。

範例
--

### 使用 `Object.create()` 實現類別繼承

下方是如何使用 `Object.create()` 去實現類別繼承的示範，此為 JavaScript 支援的單一繼承.。

```js
// Shape - 父類別
function Shape() {
  this.x = 0;
  this.y = 0;
}

// 父類別的方法
Shape.prototype.move = function (x, y) {
  this.x += x;
  this.y += y;
  console.info("Shape moved.");
};

// Rectangle - 子類別
function Rectangle() {
  Shape.call(this); // call super constructor.
}

// 子類別擴展(extends)父類別
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;

var rect = new Rectangle();

console.log("Is rect an instance of Rectangle?", rect instanceof Rectangle); // true
console.log("Is rect an instance of Shape?", rect instanceof Shape); // true
rect.move(1, 1); // Outputs, 'Shape moved.'
```

也可像 mixin 繼承多個物件。

```js
function MyClass() {
  SuperClass.call(this);
  OtherSuperClass.call(this);
}

// 繼承一個父類別
MyClass.prototype = Object.create(SuperClass.prototype);
// mixin另一個父類別
Object.assign(MyClass.prototype, OtherSuperClass.prototype);
// 重新指定建構式
MyClass.prototype.constructor = MyClass;

MyClass.prototype.myMethod = function () {
  // do a thing
};
```

Object.assign 複製 OtherSuperClass 原型上的所有屬性到 MyClass 的原型上，使所有 MyClass 的實例都能使用。Object.assign 為 ES2015 標準且有 polyfill。如需支援較舊的瀏覽器，可使用第三方套件實現如 jQuery.extend() 或 .assign() 。

### `propertiesObject` 參數的使用

```js
var o;

// 建立以null為原型的物件
o = Object.create(null);

o = {};
// 等同於:
o = Object.create(Object.prototype);

// Example where we create an object with a couple of sample properties.
// (Note that the second parameter maps keys to \*property descriptors\*.)
o = Object.create(Object.prototype, {
  // foo 為數值屬性
  foo: { writable: true, configurable: true, value: "hello" },
  // bar 為 getter-and-setter 訪問屬性
  bar: {
    configurable: false,
    get: function () {
      return 10;
    },
    set: function (value) {
      console.log("Setting `o.bar` to", value);
    },
    /\* with ES5 Accessors our code can look like this
 get function() { return 10; },
 set function(value) { console.log('setting `o.bar` to', value); } \*/
  },
});

function Constructor() {}
o = new Constructor();
// 等同於:
o = Object.create(Constructor.prototype);
// Of course, if there is actual initialization code in the
// Constructor function, the Object.create() cannot reflect it

// 創建一個新物件，指定原型是全新的空物件，並加入值為 42 的屬性'p'
o = Object.create({}, { p: { value: 42 } });

// 屬性敘述元 writable, enumerable , configurable 未定義，預設皆為 false
o.p = 24;
o.p;
// 42

o.q = 12;
for (var prop in o) {
  console.log(prop);
}
// 'q'

delete o.p;
// false

// to specify an ES3 property
o2 = Object.create(
  {},
  {
    p: {
      value: 42,
      writable: true,
      enumerable: true,
      configurable: true,
    },
  },
);
```

Polyfill
--------

此 polyfill 涵蓋了主要的使用情境：指定一個原型創建一個新的物件，第二個參數為選用。

要注意的是在 ES5 的 `Object.create` 中，`[[Prototype]]` 可以為 `null`，但在 ECMAScript 5 以前的版本，polyfill 會因為繼承限制（limitation inherent）而不支援此情形。

```js
if (typeof Object.create !== "function") {
  Object.create = function (proto, propertiesObject) {
    if (
      !(
        proto === null ||
        typeof proto === "object" ||
        typeof proto === "function"
      )
    ) {
      throw TypeError("Argument must be an object, or null");
    }
    var temp = new Object();
    temp.__proto__ = proto;
    if (typeof propertiesObject === "object")
      Object.defineProperties(temp, propertiesObject);
    return temp;
  };
}
```

規範
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-object.create |

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

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

參閱
--

* `Object.defineProperty()`
* `Object.defineProperties()` (en-US)
* `Object.prototype.isPrototypeOf()` (en-US)
* John Resig's post on getPrototypeOf()


Object.getPrototypeOf() - JavaScript

Object.getPrototypeOf()
=======================

**`Object.getPrototypeOf()`** 回傳指定物件的原型，換句話說，就是取得該物件的 `[[Prototype]]` 屬性的值).

表達式
---
```

Object.getPrototypeOf(obj)
```

### 參數

`obj`

欲取得原型的物件。

範例
--

```js
var proto = {};
var obj = Object.create(proto);
Object.getPrototypeOf(obj) === proto; // true
```

備註
--

如果 `obj` 參數在 ES5 並不是物件時會拋出 `TypeError` (en-US) 例外，同樣狀況在 ES6 時該參數將會被強制轉換成 `Object` (en-US)。

```js
Object.getPrototypeOf("foo");
// TypeError: "foo" is not an object (ES5 code)
Object.getPrototypeOf("foo");
// String.prototype (ES6 code)
```

規範
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-object.getprototypeof |

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

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

Opera 註
-------

雖然舊版的 Opera 並不支援 `Object.getPrototypeOf()`，但是 Opera 從 Opera 10.50 支援非標準的 `Object.prototype.__proto__` (en-US) 屬性。

參見
--

* `Object.prototype.isPrototypeOf()` (en-US)
* `Object.setPrototypeOf()` (en-US)
實驗性質
* `Object.prototype.__proto__` (en-US)
* John Resig 的文章－－《 getPrototypeOf 》
* `Reflect.getPrototypeOf()` (en-US)


Object.preventExtensions() - JavaScript

Object.preventExtensions()
==========================

**`Object.preventExtensions()`** 用來避免物件被新增新的屬性。

語法
--

```js
Object.preventExtensions(obj)
```

### 參數

`obj`

要被用作無法擴充的物件。

描述
--

物件如果可以被增加新的屬性，我們稱它可以被擴充(extensible)。`Object.preventExtensions()` 標註物件使它無法被擴充，所以在它被標註為無法擴充當下，它將無法再增加新的屬性。不過注意一點，在一般狀況下，被標註為無法擴充的物件，其屬性仍可被刪除(*deleted*)。嘗試去增加屬性將會導致失敗，可能會沒有結果產生，或是傳回一個 `TypeError` (en-US) (最常見，但並不是一定，當在strict mode (en-US))。

`Object.preventExtensions()` 只有避免物件被增加屬性，屬性仍可以被增加至 `object prototype`。不過，呼叫 `Object.preventExtensions()` 使用在物件上，就可以使其 `Object.prototype.__proto__` (en-US)
已棄用
 屬性無法被擴充。

如果能把可擴充物件，轉成無法擴充物件，在 ECMAScript 5 規範中，它並沒有任何方法轉回來。

範例
--

```js
// Object.preventExtensions 傳回一個被無法擴充的物件
var obj = {};
var obj2 = Object.preventExtensions(obj);
obj === obj2; // true

// 預設下，物件可以被擴充
var empty = {};
Object.isExtensible(empty); // === true

// ...但是以下情況之後，無法再被變更。
Object.preventExtensions(empty);
Object.isExtensible(empty); // === false

// Object.defineProperty throws 當為無法擴充的物件增加屬性
var nonExtensible = { removable: true };
Object.preventExtensions(nonExtensible);
Object.defineProperty(nonExtensible, "new", { value: 8675309 }); // throws a TypeError

// 在 strict mode 中，嘗試去新增屬性給無法擴充物件，將 throws 出一個 TypeError。
function fail() {
  "use strict";
  nonExtensible.newProperty = "FAIL"; // throws a TypeError
}
fail();

// EXTENSION (only works in engines supporting \_\_proto\_\_
// (which is deprecated. Use Object.getPrototypeOf instead)):
// A non-extensible object's prototype is immutable.
var fixed = Object.preventExtensions({});
fixed.__proto__ = { oh: "hai" }; // throws a TypeError
```

筆記
--

在 ES5 中，如果給祝個方法的參數為非物件，它將造成一個 `TypeError` (en-US) 。不過在 ES6 中，非物件參數會被正常處理。另外，如果它原本就是個無法擴充物件，就只是回傳本身。

```js
Object.preventExtensions(1);
// TypeError: 1 is not an object (ES5 code)

Object.preventExtensions(1);
// 1 (ES6 code)
```

規範
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-object.preventextensions |

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

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

參見
--

* `Object.isExtensible()` (en-US)
* `Object.seal()` (en-US)
* `Object.isSealed()` (en-US)
* `Object.freeze()`
* `Object.isFrozen()` (en-US)
* `Reflect.preventExtensions()` (en-US)


Object.assign() - JavaScript

Object.assign()
===============

**`Object.assign()`** 被用來複製一個或多個物件自身所有可數的屬性到另一個目標物件。回傳的值為該目標物件。

語法
--

```js
Object.assign(target, ...sources)
```

### 參數

`target`

目標物件

`sources`

來源物件

### 回傳值

合併目標物件及(多個)來源物件所得到的最終物件。

說明
--

如果在目標物件裡的屬性名稱(key)和來源物件的屬性名稱相同，將會被覆寫。若來源物件之間又有相同的屬性名稱，則後者會將前者覆寫。

`Object.assign()`只會從來源物件將自身可列舉的屬性複製到目標物件。此函式方法(method) 使用來源物件的`[[Get]]`事件和目標物件的`[[Set]]`事件，使它將會執行 getters 和 setters。因此，這邊的指派(*assigns*)屬性不只是複製或定義新屬性。若在合併包含 getters 的來源物件時，這個事件可能就不適合用來合併屬性。至於複製屬性的定義(包含其可列舉性)到各屬性，反倒是會用到 `Object.getOwnPropertyDescriptor()` (en-US) 和 `Object.defineProperty()` 。

`String` 和 `Symbol` (en-US) 類型的屬性都會被複製。

若發生錯誤，例如: 當一個屬性不可被寫入時，將會引發 `TypeError` (en-US) 的錯誤，且目標物件剩餘的屬性將不會改變。

注意: `Object.assign()` 不會在來源物件屬性的值為`null` (en-US) 或 `undefined` (en-US) 的時候拋出錯誤。

範例
--

### 複製物件

```js
var obj = { a: 1 };
var copy = Object.assign({}, obj);
console.log(copy); // { a: 1 }
```

### 警告:非深層複製

深層複製(deep clone)需要使用其他的替代方案，因為 `Object.assign()` 僅複製屬性值。若來源物件的值參照到一個子物件，它只會複製該子物件的參照。

```js
function test() {
  let a = { b: { c: 4 }, d: { e: { f: 1 } } };
  let g = Object.assign({}, a); // 淺層
  let h = JSON.parse(JSON.stringify(a)); // 深層
  console.log(g.d); // { e: { f: 1 } }
  g.d.e = 32;
  console.log("g.d.e set to 32."); // g.d.e set to 32.
  console.log(g); // { b: { c: 4 }, d: { e: 32 } }
  console.log(a); // { b: { c: 4 }, d: { e: 32 } }
  console.log(h); // { b: { c: 4 }, d: { e: { f: 1 } } }
  h.d.e = 54;
  console.log("h.d.e set to 54."); // h.d.e set to 54.
  console.log(g); // { b: { c: 4 }, d: { e: 32 } }
  console.log(a); // { b: { c: 4 }, d: { e: 32 } }
  console.log(h); // { b: { c: 4 }, d: { e: 54 } }
}
test();
```

### 合併物件

```js
var o1 = { a: 1 };
var o2 = { b: 2 };
var o3 = { c: 3 };

var obj = Object.assign(o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }
console.log(o1); // { a: 1, b: 2, c: 3 }, 目標物件本身也被改變。
```

### 有相同屬性時合併物件

```js
var o1 = { a: 1, b: 1, c: 1 };
var o2 = { b: 2, c: 2 };
var o3 = { c: 3 };

var obj = Object.assign({}, o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }，屬性c為o3.c的值，最後一個出現的屬性c。
```

所有的屬性會被後方相同屬性名稱的值覆寫。

### 複製 Symbol 型別的屬性

```js
var o1 = { a: 1 };
var o2 = { [Symbol("foo")]: 2 };

var obj = Object.assign({}, o1, o2);
console.log(obj); // { a : 1, [Symbol("foo")]: 2 } (cf. bug 1207182 on Firefox)
Object.getOwnPropertySymbols(obj); // [Symbol(foo)]非不在
```

### 在屬性鏈中的不可列舉屬性不會被複製

```js
var obj = Object.create(
  { foo: 1 },
  {
    // foo 是 obj 的屬性鏈。
    bar: {
      value: 2, // bar 是不可列舉的屬性，因為enumerable預設為false。
    },
    baz: {
      value: 3,
      enumerable: true, // baz 是自身可列舉的屬性。
    },
  },
);

var copy = Object.assign({}, obj);
console.log(copy); // { baz: 3 }
```

### 原始型別會被包成物件

```js
var v1 = "abc";
var v2 = true;
var v3 = 10;
var v4 = Symbol("foo");

var obj = Object.assign({}, v1, null, v2, undefined, v3, v4);
// 原始型別會被打包，null和undefined則會被忽略。
// 注意: 只有打包成物件的字串是可列舉的，即可被複製的。
console.log(obj); // { "0": "a", "1": "b", "2": "c" }
```

### 任何異常將會中斷正進行的複製程序

```js
var target = Object.defineProperty({}, "foo", {
  value: 1,
  writable: false,
}); // target.foo 是 read-only (唯讀)屬性

Object.assign(target, { bar: 2 }, { foo2: 3, foo: 3, foo3: 3 }, { baz: 4 });
// TypeError: "foo" 是 read-only
// 在指派值給 target.foo 時，異常(Exception)會被拋出。

console.log(target.bar); // 2, 第一個來源物件複製成功。
console.log(target.foo2); // 3, 第二個來源物件的第一個屬性複製成功。
console.log(target.foo); // 1, 異常在這裡拋出。
console.log(target.foo3); // undefined, 複製程式已中斷，複製失敗。
console.log(target.baz); // undefined, 第三個來源物件也不會被複製。
```

### 複製的存取程序

```js
var obj = {
  foo: 1,
  get bar() {
    return 2;
  },
};

var copy = Object.assign({}, obj);
console.log(copy);
// { foo: 1, bar: 2 }， copy.bar的值，是obj.bar的getter回傳的值。

// 這個函式用來複製完整的描述內容。
function completeAssign(target, ...sources) {
  sources.forEach((source) => {
    let descriptors = Object.keys(source).reduce((descriptors, key) => {
      descriptors[key] = Object.getOwnPropertyDescriptor(source, key);
      return descriptors;
    }, {});
    // Object.assign 預設會複製可列舉的Symbols。
    Object.getOwnPropertySymbols(source).forEach((sym) => {
      let descriptor = Object.getOwnPropertyDescriptor(source, sym);
      if (descriptor.enumerable) {
        descriptors[sym] = descriptor;
      }
    });
    Object.defineProperties(target, descriptors);
  });
  return target;
}

var copy = completeAssign({}, obj);
console.log(copy);
// { foo:1, get bar() { return 2 } }
```

Polyfill
--------

polyfill (en-US) 不支援 Symbol 屬性，因為 ES5 沒有 Symbol 型別。

```js
if (typeof Object.assign != "function") {
  Object.assign = function (target, varArgs) {
    // .length of function is 2
    "use strict";
    if (target == null) {
      // TypeError if undefined or null
      throw new TypeError("Cannot convert undefined or null to object");
    }

    var to = Object(target);

    for (var index = 1; index < arguments.length; index++) {
      var nextSource = arguments[index];

      if (nextSource != null) {
        // Skip over if undefined or null
        for (var nextKey in nextSource) {
          // Avoid bugs when hasOwnProperty is shadowed
          if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
            to[nextKey] = nextSource[nextKey];
          }
        }
      }
    }
    return to;
  };
}
```

規格
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-object.assign |

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

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

參閱
--

* `Object.defineProperties()` (en-US)
* Enumerability and ownership of properties (en-US)