getter - JavaScript

getter
======

**`get`** 語法會將物件屬性，綁定到屬性被檢索時，所呼叫的函式。

嘗試一下
----

語法
--
```

{get prop() { ... } }
{get [expression]() { ... } }
```

### 參數

`prop`

要綁定到給定函式的名稱。

expression

自 ECMAScript 2015 開始，可以用計算屬性名稱（computed property name），綁定到給定函式。

敘述
--

有時候，物件的屬性可能需要回傳動態數值、或要在不使用明確的方法呼叫下，反映出內部變數的狀態。在 JavaScript 可以用 *getter* 達到這個目的。儘管可以用 getter 與 setter 的關聯建立虛擬屬性的類型，但 getter 無法被綁定到同時擁有實際數值的屬性。

使用 `get` 語法時，請注意以下情況：

* 可以擁有一個以數字或字串為代表的標示符；
* 最少要有零個參數（請參見 Incompatible ES5 change: literal getter and setter functions must now have exactly zero or one arguments 的詳細資料）
* 不能以有另一個 `get` 的 object literal、或相同屬性入口（data entry）的 data 形式出現（不能使用 `{ get x() { }, get x() { } }` and `{ x: ..., get x() { } }`）。

getter 可以用 `delete` 操作符移除。

示例
--

### 在物件初始器（object initializers）內定義新物件的 getter

這程式碼將給 `obj` 物件建立虛擬屬性 `latest`，它會回傳 `log` 陣列的最後一個單元。

```js
var obj = {
  log: ["example", "test"],
  get latest() {
    if (this.log.length == 0) return undefined;
    return this.log[this.log.length - 1];
  },
};
console.log(obj.latest); // "test".
```

請注意 `latest` 不會因為數值被指派而改變。

### 使用 `delete` 操作符移除 getter

如果想移除 getter，可以使用 `delete` 完成之：

```js
delete obj.latest;
```

### 使用 `defineProperty` 給現有物件定義 getter

若想在任何時候給現有物件添增 getter，請使用 `Object.defineProperty()`。

```js
var o = { a: 0 };

Object.defineProperty(o, "b", {
  get: function () {
    return this.a + 1;
  },
});

console.log(o.b); // Runs the getter, which yields a + 1 (which is 1)
```

### 使用計算屬性名

```js
var expr = "foo";

var obj = {
  get [expr]() {
    return "bar";
  },
};

console.log(obj.foo); // "bar"
```

### Smart / self-overwriting / lazy getters

Getter 提供了定義物件屬性的方法，但它本身並不會去自動計算，直到想訪問它。除非需要用 getter，否則數值計算會被延緩；如果不需要用到 getter，那就永遠無須支付計算的開銷。

針對屬性值 lazy 或 delay、並暫存以留作未來訪問的最佳化技巧稱作 **smart 或 memoized getter**：初次計算時會呼叫 getter、接著被暫存以便在不重算的情況下做後續訪問。這種技巧在以下情況會派上用場：

* 如果數值開銷很昂貴（例如需要大量 RAM 或 CPU 時間、產生 worker 執行緒、檢索遠端文件等）
* 如果現在並不需要數值：可能是現在用不到、或在某些情況下完全用不到。
* 如果使用的話，該數值會被訪問數次、且該數值永遠不會更改、或不應該更改。

也就是說，出於 getter 不會重新計算的理由，不要針對數值預期會改變的屬性，使用 lazy getter。

下例的物件擁有作為自己的屬性的 getter。在取得該屬性後，它會從物件被移除、並以隱式數值屬性重新增加、最後回傳之。

```js
get notifier() {
  delete this.notifier;
  return this.notifier = document.getElementById('bookmarked-notification-anchor');
},
```

針對 Firefox 程式碼，另請參見定義 `defineLazyGetter()` 函式的 XPCOMUtils.jsm 程式模塊。

### `get` 與 `defineProperty`

在使用 `classes` 時，儘管 `get` 關鍵字與 `Object.defineProperty()` 會出現相同結果，但其中有微妙的差異。

在使用 `get` 時，屬性會在物件的原型被定義；而在使用 `Object.defineProperty()` 時，屬性會在被套用的實例內定義。

```js
class Example {
  get hello() {
    return "world";
  }
}

const obj = new Example();
console.log(obj.hello);
// "world"
console.log(Object.getOwnPropertyDescriptor(obj, "hello"));
// undefined
console.log(
  Object.getOwnPropertyDescriptor(Object.getPrototypeOf(obj), "hello"),
);
// { configurable: true, enumerable: false, get: function get hello() { return 'world'; }, set: undefined }
```

規範
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-method-definitions |

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

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

參見
--

* setter
* `delete`
* `Object.defineProperty()`
* `Object.prototype.__defineGetter__()` (en-US)
* `Object.prototype.__defineSetter__()` (en-US)
* JavaScript 教學的定義 Getter 與 Setter (en-US)


箭頭函式 - JavaScript

箭頭函式
====

**箭頭函式運算式**（arrow function expression）擁有比函式運算式 (en-US)還簡短的語法。它沒有自己的 `this`、arguments (en-US)、super、new.target (en-US) 等語法。本函式運算式適用於非方法的函式，但不能被用作建構式（constructor）。

嘗試一下
----

基本語法
----
```

(參數1, 參數2, …, 參數N) => { 陳述式; }

(參數1, 參數2, …, 參數N) => 表示式;
// 等相同(參數1, 參數2, …, 參數N) => { return 表示式; }

// 只有一個參數時,括號才能不加:
(單一參數) => { 陳述式; }
單一參數 => { 陳述式; }

//若無參數，就一定要加括號:
() => { statements }
```

進階語法
----
```

// 用大括號將內容括起來，返回一個物件字面值表示法：
params => ({foo: bar})

// 支援其餘參數與預設參數
(param1, param2, ...rest) => { statements }
(param1 = defaultValue1, param2, …, paramN = defaultValueN) => {
statements }

// 也支援 parameter list 的解構
var f = ([a, b] = [1, 2], {x: c} = {x: a + b}) => a + b + c; f(); // 6
```

說明
--

也可參閱 "ES6 In Depth: Arrow functions" on hacks.mozilla.org。

箭頭函式有兩個重要的特性：更短的函式寫法與 `this` 變數的非綁定。

### 更短的函式寫法

```js
var elements = ["Hydrogen", "Helium", "Lithium", "Beryllium"];

// 這段函式會輸出[8, 6, 7, 9]這個陣列
elements.map(function (element) {
  return element.length;
});

// 上方這種一般的函式，可以被改寫成下方的箭頭函式
elements.map((element) => {
  return element.length;
}); // [8, 6, 7, 9]

// 如果輸入的參數只有一個，我們可以移除掉外面的括號
elements.map((element) => {
  return element.length;
}); // [8, 6, 7, 9]

// 當箭頭函式裡的內容只有'return'的時候，我們可以拿掉return和外面的大括號
elements.map((element) => element.length); // [8, 6, 7, 9]

// 在這個範例中，因為我們只需要length這個屬性，所以也可以使用解構賦值：
// 下方的'length'對應到我們想取得的屬性，而'lengthFooBArX'只是很普通的變數名稱,
// 可以被任意修改成你想要的名字
elements.map(({ length: lengthFooBArX }) => lengthFooBArX); // [8, 6, 7, 9]

// 上面這種解構賦值之後的參數也可以被改寫為下面這樣。但要注意的是，在這個範例中，
// 我們不是要指定'length'這個值給一個虛構的屬性，而是這個變數的名稱'length'本身就是
// 用來當成我們想從物件上取得的屬性
elements.map(({ length }) => length); // [8, 6, 7, 9]
```

### `this` 不分家

 在有箭頭函數之前，每個新函式是依據如何被呼叫來定義自己的 `this` 變數
 例如:

* 在建構子時是一個新物件
* 在呼叫嚴格模式函數時是 undefined
* 以物件方法呼叫時則為基礎物件
* 等等....

事實證明這對物件導向程式設計來說並不理想。

```js
function Person() {
  // Person() 建構式將 this 定義為它自己的一個實體
  this.age = 0;

  setInterval(function growUp() {
    // 在非嚴格模式下, growUp() 函式把 this 定義為全域物件
    // (因為那是 growUp()執行的所在)，
    // 與 Person() 建構式所定義的 this 有所不同
    this.age++;
  }, 1000);
}

var p = new Person();
```

在 ECMAScript 3/5 裡面，有關 `this` 的問題，可以透過指派 `this` 值給可以關閉的變數解決。

```js
function Person() {
  var self = this; // 有些人喜歡 `that` 而不是 `self`.
  // 選好一種取法後始終如一
  self.age = 0;

  setInterval(function growUp() {
    // 這個 callback 參考 `self` 變數，為預期中的物件。
    self.age++;
  }, 1000);
}
```

或者透過 bind 函式來綁定 `this` 變數到指定函式（以上面為例，就是 `growUp()` 函式）。

箭頭函式並不擁有自己的 `this 變`數`；`使用的 this `值來自`封閉的文本上下文，也就是說，箭頭函式遵循常規變量查找規則。因此，如果在當前範圍中搜索不到 this 變量時，他們最終會尋找其封閉範圍。

因此，在以下程式碼內，傳遞給 `setInterval` 的 箭頭函式中的`this` ，會與封閉函式的 `this` 值相同：

```js
function Person() {
  this.age = 0;

  setInterval(() => {
    this.age++; // |this| 適切的參考了Person建構式所建立的物件
  }, 1000);
}

var p = new Person();
```

#### 和嚴格模式的關係

由於 `this` 變數具有詞彙上綁定意義，所以嚴格模式的宣告對 `this` 的作用將被忽略。

```js
var f = () => {
  "use strict";
  return this;
};
f() === window; // 或是 global 物件
```

但嚴格模式的其他作用依舊存在。

#### 由 call 與 apply 函式呼叫

由於箭頭函式並沒有自己的 `this`，所以透過 `call()` 或 `apply()` 呼叫箭頭函式只能傳入參數。`thisArg` 將會被忽略。

```js
var adder = {
  base: 1,
  add: function (a) {
    var f = (v) => v + this.base;
    return f(a);
  },
  addThruCall: function (a) {
    var f = (v) => v + this.base;
    var b = {
      base: 2,
    };
    return f.call(b, a);
  },
};
console.log(adder.add(1)); // 顯示 2
console.log(adder.addThruCall(1)); // 依舊顯示 2
```

### 不綁定 `arguments`

箭頭函式並沒有自己的 `arguments` 物件 (en-US)。所以在此例中，`arguments` 只是參考到 enclosing 作用域裡面的相同變數：

```js
var arguments = [1, 2, 3];
var arr = () => arguments[0];

arr(); // 1

function foo(n) {
  var f = () => arguments[0] + n; // foo's implicit arguments binding. arguments[0] is n
  return f();
}

foo(1); // 2
```

大多時候，使用其餘參數 是取代 `arguments` 物件的較好方式。

```js
function foo(n) {
  var f = (...args) => args[0] + n;
  return f(10);
}

foo(1); // 11
```

### 將箭頭函式撰寫為方法

如同前述，箭頭函式運算式最適合用在非方法的函式。來看看如果要把它們當成方法來用，會發生什麼事：

```js
"use strict";
var obj = {
  i: 10,
  b: () => console.log(this.i, this),
  c: function () {
    console.log(this.i, this);
  },
};
obj.b(); // 印出 undefined, Window {...} (or the global object)
obj.c(); // 印出 10, Object {...}
```

箭頭函式並沒有自己的 `this`。另一個例子與 `Object.defineProperty()` 有關：

```js
"use strict";

var obj = {
  a: 10,
};

Object.defineProperty(obj, "b", {
  get: () => {
    console.log(this.a, typeof this.a, this); // undefined, 'undefined', Window {...} (or the global object)
    return this.a + 10; // represents global object 'Window', therefore 'this.a' returns 'undefined'
  },
});
```

### 使用 `new` 運算子

箭頭函式不可作為建構式使用；若使用於建構式，會在使用 `new` 時候拋出錯誤。

```js
var Foo = () => {};
var foo = new Foo(); // TypeError: Foo is not a constructor
```

### 使用 `prototype` 屬性

箭頭函式並沒有原型（`prototype`）屬性。

```js
var Foo = () => {};
console.log(Foo.prototype); // undefined
```

### 使用關鍵字 `yield`

`yield` (en-US) 關鍵字無法用於箭頭函式的 body（except when permitted within functions further nested within it）。因此，箭頭函式無法使用 generator。

函式主體（Function body）
-------------------

箭頭函式可以變成 concise body 或者平常使用的 block body。

在 concise body 裡面只需要輸入運算式，就會附上內建的回傳。在 block body 裡面就必須附上明確的 `return` 宣告。

```js
var func = (x) => x \* x; // concise 語法會內建 "return"
var func = (x, y) => {
  return x + y;
}; // block body 需要明確的 "return"
```

回傳物件字面值
-------

請注意只使用 `params => {object:literal}` 並不會按照期望般回傳物件字面值（object literal）。

```js
var func = () => { foo: 1 };               // Calling func() returns undefined!
var func = () => { foo: function() {} };   // SyntaxError: Unexpected token (
```

因為在大括弧（`{}`）裡面的文字會被解析為有序宣告（例如 `foo` 會被當作標記（label）、而不是物件的 key ）

要記得把物件字面值包在圓括弧內。

```js
var func = () => ({ foo: 1 });
var func = () => ({ foo: function () {} });
```

換行
--

箭頭函式不可以在參數及箭頭間包含換行。

```js
var func = ()
           => 1; // SyntaxError: expected expression, got '=>'
```

Parsing order
-------------

箭頭函式的箭頭儘管不是操作符，但藉著運算子優先等級，箭頭函式有著和普通函式不相同的特殊解析規則。

```js
let callback;

callback = callback || function() {}; // ok
callback = callback || () => {};      // SyntaxError: invalid arrow-function arguments
callback = callback || (() => {});    // ok
```

更多範例
----

```js
// 回傳 undefined 的箭頭函式
let empty = () => {};

(() => "foobar")(); // 回傳 "foobar"

var simple = (a) => (a > 15 ? 15 : a);
simple(16); // 15
simple(10); // 10

let max = (a, b) => (a > b ? a : b);

// Easy array filtering, mapping, ...

var arr = [5, 6, 13, 0, 1, 18, 23];
var sum = arr.reduce((a, b) => a + b); // 66
var even = arr.filter((v) => v % 2 == 0); // [6, 0, 18]
var double = arr.map((v) => v \* 2); // [10, 12, 26, 0, 2, 36, 46]
```

規範
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-arrow-function-definitions |

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

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

參閱
--

* "ES6 In Depth: Arrow functions" on hacks.mozilla.org


其餘參數 - JavaScript

其餘參數
====

**其餘參數（rest parameter）** 語法可以讓我們表示不確定數量的參數，並將其視為一個陣列。

語法
--

```js
function f(a, b, ...theArgs) {
  // ...
}
```

描述
--

如果函式的最後一個命名參數以 `...` 開頭，它會被視為一個陣列。該陣列的元素會被置於索引從 `0`（含）到的 `theArgs.length`（不含）位置，並且被視為一個函式的參數。

在上面的範例中，`theArgs` 會將函式 f 中第三個(含)以後的參數收集起來。

### 其餘參數和 `arguments` 物件的差異

以下是其餘參數和 `arguments 物件`三個主要的差異：

* 其餘參數是 `arguments` 物件被傳入到函式的時候，還沒被指定變數名稱的引數。
* `arguments` 物件不是一個實際的陣列，而 rest parameter 是陣列的實體，即 `sort`、`map`、`forEach` (en-US) 或 `pop` 可以直接在其餘參數被調用。
* `arguments` 物件自身有額外的功能，例如 `callee` 屬性。
### 將參數轉成陣列

其餘參數被介紹作為取代用 arguments 寫的範例程式。

```js
// 在有其餘參數之前，你可能見過下面的程式碼：
function f(a, b) {
  var args = Array.prototype.slice.call(arguments, f.length); // f.length 表示 arguments 的數量

  // …
}

// 現在可以寫成這樣

function f(a, b, ...args) {}
```

### 解構其餘參數 rest parameters

其餘參數可以被解構，換句話說，可以把這個陣列解開，並將各個元素取出成為個別的變數。請參考解構賦值。

```js
function f(...[a, b, c]) {
  return a + b + c;
}

f(1); // NaN (b 和 c 都是 undefined)
f(1, 2, 3); // 6
f(1, 2, 3, 4); // 6 (第四個參數不會被解構，因為解構式只有三個定義好的變數名稱)
```

範例
--

因為 `theArgs` 是一個陣列，所以它會有 `length` 屬性，作為表示參數數量：

```js
function fun1(...theArgs) {
  console.log(theArgs.length);
}

fun1(); // 0
fun1(5); // 1
fun1(5, 6, 7); // 3
```

在接下來的範例中，其餘參數被用來收集第一個之後的所有引數並存在陣列中。 在這個陣列裡的每個元素(數字)，都會和第一個參數相乘後取代原本的元素，最後再將取代元素後的陣列回傳。

```js
function multiply(multiplier, ...theArgs) {
  return theArgs.map(function (element) {
    return multiplier \* element;
  });
}

var arr = multiply(2, 1, 2, 3);
console.log(arr); // [2, 4, 6]
```

下列範例展示 `Array` 的方法可以在其餘參數上被使用，但 `arguments` 則不行。

```js
function sortRestArgs(...theArgs) {
  var sortedArgs = theArgs.sort();
  return sortedArgs;
}

console.log(sortRestArgs(5, 3, 7, 1)); // 顯示 1, 3, 5, 7

function sortArguments() {
  var sortedArgs = arguments.sort();
  return sortedArgs; // 因為前一行會因為 arguments 沒有sort()這個方法而造成TypeError，所以永遠不會執行此行。
}

console.log(sortArguments(5, 3, 7, 1)); // 會拋出 TypeError (arguments.sort is not a function)
```

為了要在 `arguments` 物件上使用 `Array` 的方法，可以將它轉換成真的 `Array` 實體，或者以 `apply()` 直接調用需要的方法。

```js
function sortArguments() {
  var args = Array.from(arguments);
  var sortedArgs = args.sort();
  return sortedArgs;
}
console.log(sortArguments(5, 3, 7, 1)); // 顯示 1, 3, 5, 7
```

規範
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-function-definitions |

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

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

參見
--

* Spread operator (en-US) (also 『`...`』)
* Arguments object (en-US)
* Array
* Functions (en-US)
* Original proposal at ecmascript.org
* JavaScript arguments object and beyond
* Destructuring assignment


setter - JavaScript

setter
======

**`set`** 語法會在物件屬性被嘗試定義時，將其屬性綁定到要呼叫的函式內。

嘗試一下
----

語法
--
```

{set prop(val) { . . . }}
{set [expression](val) { . . . }}
```

### 參數

`prop`

要綁定到給定函式的屬性名。

`val`

變數別名，該變數擁有要被嘗試安插到 `prop` 的數值。

expression

從 ECMAScript 2015 開始，可以使用計算屬性名（computed property name）表達式，綁定到給定函式。

描述
--

JavaScript 的 setter 能在嘗試修改指定屬性時，執行給定函式。Setter 最常用於和 getter 一同建立虛擬屬性（pseudo-property）。你不可能給同一個屬性賦予 setter 與實際值。

使用 `set` 語法時，請注意以下情況：

* 可以擁有一個以數字或字串為代表的標示符；
* 最少要有一個參數（請參見 Incompatible ES5 change: literal getter and setter functions must now have exactly zero or one arguments 的詳細資料）；
* 不能以有另一個 `set` 的 object literal、或相同屬性入口（data entry）的 data 形式出現（不能使用 `{ set x(v) { }, set x(v) { } }` and `{ x: ..., set x(v) { } }`）

`delete` 操作符可移除 setter。

示例
--

### 在物件初始器的新物件定義 setter

這裡會給物件 `language` 定義稱為 `current` 的虛擬屬性。在指派數值時 `log` 會和該值一同更新：

```js
var language = {
  set current(name) {
    this.log.push(name);
  },
  log: [],
};

language.current = "EN";
console.log(language.log); // ['EN']

language.current = "FA";
console.log(language.log); // ['EN', 'FA']
```

請注意 `current` is not defined and any attempts to access it will result in `undefined`.

### 使用 `delete` 操作符移除 setter

若想移除 setter 的話，可以直接使用 `delete`：

```js
delete o.current;
```

### 針對已存在屬性的 setter 使用 `defineProperty`

To append a setter to an existing object later at any time, use `Object.defineProperty()`.

```js
var o = { a: 0 };

Object.defineProperty(o, "b", {
  set: function (x) {
    this.a = x / 2;
  },
});

o.b = 10; // Runs the setter, which assigns 10 / 2 (5) to the 'a' property
console.log(o.a); // 5
```

### 使用計算屬性名

```js
var expr = "foo";

var obj = {
  baz: "bar",
  set [expr](v) {
    this.baz = v;
  },
};

console.log(obj.baz); // "bar"
obj.foo = "baz"; // 跑 setter
console.log(obj.baz); // "baz"
```

規範
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-method-definitions |

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

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

參見
--

* getter
* `delete`
* `Object.defineProperty()`
* `Object.prototype.__defineGetter__()` (en-US)
* `Object.prototype.__defineSetter__()` (en-US)
* JavaScript 教學的定義 Getters 與 Setters (en-US)


方法定義 - JavaScript

方法定義
====

自 ECMAScript 2015 開始，引入了一種於物件初始器（objects initializers）中定義方法的簡短語法。是一個將函式指派予方法名稱的簡便方式。

嘗試一下
----

語法
--
```

var obj = {
  property( parameters… ) {},
  *generator( parameters… ) {},
  async property( parameters… ) {},
  async* generator( parameters… ) {},

  // with computed keys:
  [property]( parameters… ) {},
  *[generator]( parameters… ) {},
  async [property]( parameters… ) {},

  // compare getter/setter syntax:
  get property() {},
  set property(value) {}
};
```

說明
--

這個簡短的語法和在 ECMAScript 2015 引入 getter 以及 setter 類似。

請看以下程式碼：

```js
var obj = {
  foo: function () {
    /\* code \*/
  },
  bar: function () {
    /\* code \*/
  },
};
```

你可以把它縮減為：

```js
var obj = {
  foo() {
    /\* code \*/
  },
  bar() {
    /\* code \*/
  },
};
```

### 產生器方法

產生器方法（Generator method）也可以透過簡短語法定義之。用的時候：

* 簡短語法的星號（\*）必須放在產生器方法的屬性名前面。也就是說 `* g(){}` 能動但 `g *(){}` 不行；
* 非產生器方法的定義可能不會有 `yield` 關鍵字。也就是說過往的產生器函式 (en-US)動不了、並拋出`SyntaxError` (en-US)。Always use `yield` in conjunction with the asterisk (\*).

```js
// Using a named property
var obj2 = {
  g: function\* () {
    var index = 0;
    while (true) yield index++;
  },
};

// The same object using shorthand syntax
var obj2 = {
  \*g() {
    var index = 0;
    while (true) yield index++;
  },
};

var it = obj2.g();
console.log(it.next().value); // 0
console.log(it.next().value); // 1
```

### Async 方法

Async 方法 也可以透過簡短語法定義。

```js
// Using a named property
var obj3 = {
  f: async function () {
    await some_promise;
  },
};

// The same object using shorthand syntax
var obj3 = {
  async f() {
    await some_promise;
  },
};
```

### Async generator methods

Generator methods can also be async.

```js
var obj4 = {
  f: async function\* () {
    yield 1;
    yield 2;
    yield 3;
  },
};

// The same object using shorthand syntax
var obj4 = {
  async \*f() {
    yield 1;
    yield 2;
    yield 3;
  },
};
```

### Method definitions are not constructable

All method definitions are not constructors and will throw a `TypeError` (en-US) if you try to instantiate them.

```js
var obj = {
  method() {},
};
new obj.method(); // TypeError: obj.method is not a constructor

var obj = {
  \*g() {},
};
new obj.g(); // TypeError: obj.g is not a constructor (changed in ES2016)
```

範例
--

### Simple test case

```js
var obj = {
  a: "foo",
  b() {
    return this.a;
  },
};
console.log(obj.b()); // "foo"
```

### Computed property names

The shorthand syntax also supports computed property names.

```js
var bar = {
  foo0: function () {
    return 0;
  },
  foo1() {
    return 1;
  },
  ["foo" + 2]() {
    return 2;
  },
};

console.log(bar.foo0()); // 0
console.log(bar.foo1()); // 1
console.log(bar.foo2()); // 2
```

規範
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-method-definitions |

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

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

參見
--

* `get`
* `set`
* Lexical grammar


RangeError: invalid array length - JavaScript

RangeError: invalid array length
================================

訊息
--
```

RangeError: Array length must be a finite positive integer (Edge)
RangeError: invalid array length (Firefox)
RangeError: Invalid array length (Chrome)
RangeError: Invalid array buffer length (Chrome)
```

錯誤類型
----

`RangeError`哪裡錯了?
-----

一個無效的陣列長度可能發生於以下幾種情形:

* 建立了一個長度為負或大於等於 2^32 的 `Array` 或 `ArrayBuffer`
* 將 `Array.length` 屬性設為負值或大於等於 2^32

為什麼 `Array` 和 `ArrayBuffer` 的長度有限? `Array` 和 `ArrayBuffer` 的屬性以一個 32 位元的非負整數表使，因此僅能儲存 0 到 2^32 - 1 的數值。

If you are creating an `Array`, using the constructor, you probably want to use the literal notation instead, as the first argument is interpreted as the length of the `Array`.

Otherwise, you might want to clamp the length before setting the length property, or using it as argument of the constructor.

示例
--

### 無效的案例

```js
new Array(Math.pow(2, 40));
new Array(-1);
new ArrayBuffer(Math.pow(2, 32)); // 32-bit system
new ArrayBuffer(-1);

const a = [];
a.length = a.length - 1; // set the length property to -1

const b = new Array(Math.pow(2, 32) - 1);
b.length = b.length + 1; // set the length property to 2^32
b.length = 2.5; // set the length property to a floating-point number

const c = new Array(2.5); // pass a floating-point number
```

### 有效的案例

```js
[Math.pow(2, 40)]; // [ 1099511627776 ]
[-1]; // [ -1 ]
new ArrayBuffer(Math.pow(2, 32) - 1);
new ArrayBuffer(Math.pow(2, 33)); // 64-bit systems after Firefox 89
new ArrayBuffer(0);

const a = [];
a.length = Math.max(0, a.length - 1);

const b = new Array(Math.pow(2, 32) - 1);
b.length = Math.min(0xffffffff, b.length + 1);
// 0xffffffff 是 2^32 - 1 的十六進位表示
// 也可以寫成 (-1 >>> 0)

b.length = 3;

const c = new Array(3);
```

參見
--

* `Array`
* `Array.length`
* `ArrayBuffer`


SyntaxError: return not in function - JavaScript

SyntaxError: return not in function
===================================

訊息
--
```

SyntaxError: return not in function
SyntaxError: yield not in function
```

錯誤類型
----

`SyntaxError` (en-US)哪裡錯了？
-----

`return` 或 `yield` (en-US) 宣告在函式以外的地方被呼叫。也許少寫了一個大括號？`return` 與 `yield` 宣告必須要寫在函式裡面，因為它們結束（或暫停並恢復）函式的執行，並且回傳了特定值。

實例
--

```js
var cheer = function(score) {
  if (score === 147)
    return "Maximum!";
  };
  if (score > 100) {
    return "Century!";
  }
}

// SyntaxError: return not in function
```

乍看之下大括號寫對了，但其實在第一個 `if` 的後面，少了一個 `{`。正確的寫法應該是：

```js
var cheer = function (score) {
  if (score === 147) {
    return "Maximum!";
  }
  if (score > 100) {
    return "Century!";
  }
};
```

參見
--

* `return`
* `yield` (en-US)


TypeError: "x" has no properties - JavaScript

TypeError: "x" has no properties
================================

訊息
--
```

TypeError: null has no properties
TypeError: undefined has no properties
```

錯誤類型
----

`TypeError` (en-US).

哪裡錯了？
-----

`null` (en-US) 與 `undefined` (en-US) 並沒有可訪問的屬性。

示例
--

```js
null.foo;
// TypeError: null has no properties

undefined.bar;
// TypeError: undefined has no properties
```

參見
--

* `null` (en-US)
* `undefined` (en-US)


SyntaxError: redeclaration of formal parameter "x" - JavaScript

SyntaxError: redeclaration of formal parameter "x"
==================================================

訊息
--
```

SyntaxError: redeclaration of formal parameter "x" (Firefox)
SyntaxError: Identifier "x" has already been declared (Chrome)
```

錯誤類型
----

`SyntaxError` (en-US)哪裡錯了？
-----

當相同的變數名作為函式的參數、接著又在函式體（function body）內用了 `let` 重複宣告並指派時出現。在 JavaScript 裡面，不允許在相同的函式、或是作用域區塊（block scope）內重複宣告相同的 `let` 變數。

實例
--

在這裡，「arg」變數的參數被重複宣告。

```js
function f(arg) {
  let arg = "foo";
}

// SyntaxError: redeclaration of formal parameter "arg"
```

If you want to change the value of "arg" in the function body, you can do so, but you do not need to declare the same variable again. In other words: you can omit the `let` keyword. If you want to create a new variable, you need to rename it as conflicts with the function parameter already.

```js
function f(arg) {
  arg = "foo";
}

function f(arg) {
  let bar = "foo";
}
```

相容性註解
-----

* 在 Firefox 49 之前，這個錯誤被歸為 `TypeError` (en-US)。（Firefox bug 1275240）
參見
--

* `let`
* `const`
* `var`
* 在 JavaScript 教學內宣告變數


TypeError: "x" is not a function - JavaScript

TypeError: "x" is not a function
================================

訊息
--
```

TypeError: "x" is not a function
```

錯誤類型
----

`TypeError` (en-US).

哪裡錯了？
-----

你想以函式呼叫一個數值，但該數值其實不是函式。程式碼期望你給出函式，但這份期望落空了。

也許打錯了函式的名字？也許呼叫的物件並沒有這個函式？例如說 JavaScript 物件並沒有 `map` 函式，但 JavaScript Array（陣列）物件則有。

許多內建函式都需要回呼（callback）的函式。為了讓下面的方法順利運作，你需要為它們提供函式：

* 如果是 `Array` 或 `TypedArray` (en-US) 物件：
    + `Array.prototype.every()` (en-US)、`Array.prototype.some()` (en-US)、`Array.prototype.forEach()` (en-US)、`Array.prototype.map()`、`Array.prototype.filter()`、`Array.prototype.reduce()`、`Array.prototype.reduceRight()` (en-US)、`Array.prototype.find()`
* 如果是 `Map` 與 `Set` 物件：
    + `Map.prototype.forEach()` (en-US) 與 `Set.prototype.forEach()` (en-US)
實例
--

### 函式的名字打錯了

這種事太常發生了。下例就有個方法打錯：

```js
var x = document.getElementByID("foo");
// TypeError: document.getElementByID is not a function
```

該函式的正確名字為 `getElementById`：

```js
var x = document.getElementById("foo");
```

### 函式呼叫到錯誤的物件

某些方法需要你提供回呼的函式，該函式只能作用於特定物件。以本例而言，我們使用的 `Array.prototype.map()` 就只能作用於 `Array` 物件。

```js
var obj = { a: 13, b: 37, c: 42 };

obj.map(function (num) {
  return num \* 2;
});

// TypeError: obj.map is not a function
```

請改用陣列：

```js
var numbers = [1, 4, 9];

numbers.map(function (num) {
  return num \* 2;
});

// Array [ 2, 8, 18 ]
```

參見
--

* Functions (en-US)


ReferenceError: "x" is not defined - JavaScript

ReferenceError: "x" is not defined
==================================

訊息
--
```

ReferenceError: "x" is not defined
```

錯誤類型
----

`ReferenceError` (en-US).

哪裡錯了？
-----

有個地方參照到不存在的變數了。這個變數需要宣告、或確定在目前腳本、或在 scope (en-US) 裡可用。

**備註：** 如果要使用函式庫（例如 jQuery）的話，請確定在你使用諸如 $ 這樣的函式庫變數前，就已載入完畢。把載入函式庫的 `<script>` 標籤，放在你使用的程式碼之前。

實例
--

### 變數未宣告

```js
foo.substring(1); // ReferenceError: foo is not defined
```

"foo" 變數在任何地方都沒被定義到。它需要字串使 `String.prototype.substring()` (en-US) 得以運作。

```js
var foo = "bar";
foo.substring(1); // "ar"
```

### 作用域錯誤

A variable need to be available in the current context of execution. Variables defined inside a function (en-US) cannot be accessed from anywhere outside the function, because the variable is defined only in the scope of the function

```js
function numbers() {
  var num1 = 2,
    num2 = 3;
  return num1 + num2;
}

console.log(num1); // ReferenceError num1 is not defined.
```

However, a function can access all variables and functions defined inside the scope in which it is defined. In other words, a function defined in the global scope can access all variables defined in the global scope.

```js
var num1 = 2,
  num2 = 3;

function numbers() {
  return num1 + num2;
}

console.log(num1); // 2
```

參閱
--

* Scope (en-US)
* Declaring variables in the JavaScript Guide
* Function scope in the JavaScript Guide


TypeError: "x" is (not) "y" - JavaScript

TypeError: "x" is (not) "y"
===========================

錯誤類型
----

`TypeError` (en-US)哪裡錯了？
-----

有一個意想不到的類型。這與 `undefined` (en-US) 或 `null` (en-US) 值經常發生。

另外，某些方法，如 `Object.create()` 或 `Symbol.keyFor()` (en-US) 要求特定類型，即必須提供。

實例
--

### 無效的情況下

```js
// undefined 和 null 的情況下在其上的子方法不起作用
var foo = undefined;
foo.substring(1); // TypeError: foo is undefined

var foo = null;
foo.substring(1); // TypeError: foo is null

// 某些方法可能要求特定類型
var foo = {};
Symbol.keyFor(foo); // TypeError: foo is not a symbol

var foo = "bar";
Object.create(foo); // TypeError: "foo" is not an object or null
```

### 修復問題

為了解決空指針 `undefined` 或 `null` 值，可以使用 typeof 運算符，例如。 operator, for example.

```js
if (typeof foo !== "undefined") {
  // 現在我們知道foo被定義，我們可以繼續進行。
}
```

參見
--

* `undefined` (en-US)
* `null` (en-US)


SyntaxError: missing } after property list - JavaScript

SyntaxError: missing } after property list
==========================================

訊息
--
```

SyntaxError: missing } after property list
```

錯誤類型
----

`SyntaxError` (en-US)何處出錯?
-----

在物件初始化時，語法錯誤。 實際上可能遺漏一個大括號或是逗號。 例如， 同時檢查大括弧以及逗號是否以正確的順序關閉。 縮排或是有規則的排序代碼是有幫助你找出複雜的代碼錯誤。

範例
--

### 忘記逗號

有時候，在初始化物件時，缺少一個逗號：

```js
var obj = {
  a: 1,
  b: { myProp: 2 }
  c: 3
};
```

Correct would be:

```js
var obj = {
  a: 1,
  b: { myProp: 2 },
  c: 3,
};
```

參見
--

* Object initializer (en-US)


InternalError: too much recursion - JavaScript

InternalError: too much recursion
=================================

訊息
--
```

InternalError: too much recursion
```

錯誤類型
----

`InternalError` (en-US)哪裡錯了？
-----

一個呼叫自己的函式稱為*遞迴函式*（recursive function）。在某些方面，遞迴和迴圈很像。它們都需要在指定條件（以避免無窮迴圈，或是本例的無窮遞迴）下，重複執行數次相同的程式碼。如果遞迴執行太多次、或成為無窮遞迴的話，JavaScript 就會出現這個錯誤。

實例
--

以下的遞迴函式，會根據終止條件，而運行十次。

```js
function loop(x) {
  if (x >= 10)
    // "x >= 10" 是終止條件
    return;
  // do stuff
  loop(x + 1); // 遞迴呼叫
}
loop(0);
```

如果把終止條件的次數設得太高，函式就不會運作了：

```js
function loop(x) {
  if (x >= 1000000000000) return;
  // do stuff
  loop(x + 1);
}
loop(0);

// InternalError: too much recursion
```

參見
--

* Recursion
* 遞迴函式


使用 Promise - JavaScript

使用 Promise
==========

`Promise` 是一個表示非同步運算的最終完成或失敗的物件。由於多數人使用預建立的 Promise，這個導覽會先講解回傳 Promise 的使用方式，之後再介紹如何建立。

基本上，一個 Promise 是一個根據附加給他的 Callback 回傳的物件，以取代傳遞 Callback 到這個函數。

舉例來說，下方的範例若用舊方式應該會有兩個 Callback，並根據成功或失敗來決定使用哪個：

```js
function successCallback(result) {
  console.log("It succeeded with " + result);
}

function failureCallback(error) {
  console.log("It failed with " + error);
}

doSomething(successCallback, failureCallback);
```

而新作法會回傳一個 Promise，這樣你就可以附加 Callback：

```js
let promise = doSomething();
promise.then(successCallback, failureCallback);
```

再簡單點：

```js
doSomething().then(successCallback, failureCallback);
```

我們稱之為 *非同步函數呼叫*。這個做法有許多好處，我們接下來看看。

保證
--

不如舊做法，一個 Promise 有這些保證：

* Callback 不會在當次的迴圈運行結束前呼叫。
* Callback 用 .then 添加，在非同步運算結束*後*呼叫，像前面那樣。
* 複 Callback 可以透過重複呼叫 .then 達成。

但 Promise 主要的立即好處是串連。

串連
--

有個常見的需求是依序呼叫兩個以上的非同步函數，我們稱之為建立 *Promise 鏈*。

看看魔術：`then` 函數回傳一個新的 Promise，不同於原本。

```js
let promise = doSomething();
let promise2 = promise.then(successCallback, failureCallback);
```

或

```js
let promise2 = doSomething().then(successCallback, failureCallback);
```

第二個 Promise 不只代表 `doSomething()` 完成，還有`successCallback` 或 `failureCallback` ，這兩個非同步函數回傳另一個 Promise。如此一來，任何 Callback 附加給 `promise2` 會被排在 `successCallback` 或`failureCallback` 之後。

基本上，每個 Promise 代表著鏈中另外一個非同步函數的完成。

在古時候，多個非同步函數會使用 Callback 方式，導致波動拳問題：

```js
doSomething(function (result) {
  doSomethingElse(
    result,
    function (newResult) {
      doThirdThing(
        newResult,
        function (finalResult) {
          console.log("Got the final result: " + finalResult);
        },
        failureCallback,
      );
    },
    failureCallback,
  );
}, failureCallback);
```

有了新方法，我們附加 Callback 到回傳的 Promise 上，來製造 *Promise 鏈*：

```js
doSomething()
  .then(function (result) {
    return doSomethingElse(result);
  })
  .then(function (newResult) {
    return doThirdThing(newResult);
  })
  .then(function (finalResult) {
    console.log("Got the final result: " + finalResult);
  })
  .catch(failureCallback);
```

`then` 的函數是選用的，以及 `catch(failureCallback)` 是 `then(null, failureCallback)` 的簡寫。你也許會想用箭頭函數取代：

```js
doSomething()
  .then((result) => doSomethingElse(result))
  .then((newResult) => doThirdThing(newResult))
  .then((finalResult) => {
    console.log(`Got the final result: ${finalResult}`);
  })
  .catch(failureCallback);
```

**注意**：永遠要回傳結果，否則 Callback 不會獲得前一個 Promise 的結果。

### 獲錯後串接

失敗*後*的串接是可行的，也就是說 `catch` 會非常好用，即使鏈中出錯。看看這個範例：

```js
new Promise((resolve, reject) => {
  console.log("Initial");

  resolve();
})
  .then(() => {
    throw new Error("Something failed");

    console.log("Do this");
  })
  .catch(() => {
    console.log("Do that");
  })
  .then(() => {
    console.log("Do this whatever happened before");
  });
```

他會輸出：
```

Initial
Do that
Do this whatever happened before
```

注意「Do this」沒有被輸出，因為「Something failed」錯誤導致拒絕。

錯誤傳遞
----

在波動拳狀況中，你可能會看到三次 `failureCallback` ，在 Promise 鏈中只需要在尾端使用一次：

```js
doSomething()
  .then((result) => doSomethingElse(result))
  .then((newResult) => doThirdThing(newResult))
  .then((finalResult) => console.log(`Got the final result: ${finalResult}`))
  .catch(failureCallback);
```

基本上，一個 Promise 鏈遇到錯誤時會往下尋找 Catch 處理器。這是經過模組化的非同步程式：

```js
try {
  let result = syncDoSomething();
  let newResult = syncDoSomethingElse(result);
  let finalResult = syncDoThirdThing(newResult);
  console.log(`Got the final result: ${finalResult}`);
} catch (error) {
  failureCallback(error);
}
```

在 ECMAScript 2017 中，在有 `async`/`await` 語法糖的同步程式達到高峰：

```js
async function foo() {
  try {
    let result = await doSomething();
    let newResult = await doSomethingElse(result);
    let finalResult = await doThirdThing(newResult);
    console.log(`Got the final result: ${finalResult}`);
  } catch (error) {
    failureCallback(error);
  }
}
```

這基於 Promise，例如 `doSomething()` 和之前一樣。你可以閱讀在這裡閱讀更多。

Promise 藉由捕捉所有錯誤，包含例外和程式錯誤，解決了 Callback 地獄的缺點。這是非同步運算的基本特性。

在舊有 API 上建立 Promise
-------------------

`Promise` 可以透過建構子建立。所以用建構子包裹舊的 API 即可。

在理想情況，所有非同步函數都會回傳 Promise，然而許多 API 仍然用舊的方式來傳遞成功、失敗 Callback，有個典型的例子是`setTimeout()` (en-US) ：

```js
setTimeout(() => saySomething("10 seconds passed"), 10000);
```

混合古代 Callback 和 Promise 是有問題的。如果 `saySomething` 失敗或有程式錯誤，那不會有任何錯誤被捕捉。

幸運地，我們可以用 Promise 包裹他，最好盡可能的在最底層包裹，並永遠不要再直接呼叫他們：

```js
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

wait(10000)
  .then(() => saySomething("10 seconds"))
  .catch(failureCallback);
```

基本上，Promise 建構子需要一個運作函數來正規地處理或拒絕 Promise。但因為 `setTimeout` 不會失敗，所以我們捨棄 reject。

組合
--

`Promise.resolve()` 和 `Promise.reject()` 是用來正規地建立已經處理或拒絕的 Promise。他們在某些情況特別有用。

`Promise.all()` 和 `Promise.race()` 是兩個組合工具用於使 Promise 平行運作。

連續關聯是可行的，這是極簡 JavaScript 範例：

```js
[func1, func2].reduce((p, f) => p.then(f), Promise.resolve());
```

基本上，我們摺疊（Reduce）一個非同步函數陣列成一個 Promise 鏈：`Promise.resolve().then(func1).then(func2);`

這可以用可重用的構成函數完成，通常用函數式編程：

```js
let applyAsync = (acc, val) => acc.then(val);
let composeAsync =
  (...funcs) =>
  (x) =>
    funcs.reduce(applyAsync, Promise.resolve(x));
```

`composeAsync` 接受任何數量的函數作為參數，並回傳一個接受一個初始值用來傳給組合的新函數。這個好處是無論其中函數是非同步或否，都會保證用正確的順序執行：

```js
let transformData = composeAsync(func1, asyncFunc1, asyncFunc2, func2);
transformData(data);
```

ECMAScript 2017 中連續組合利用 async/await 更簡單：

```js
for (let f of [func1, func2]) {
  await f();
}
```

計時
--

為了避免意外，傳給 `then` 的函數不會被同步地呼叫，即使是完成的 Promise：

```js
Promise.resolve().then(() => console.log(2));
console.log(1); // 1, 2
```

被傳入的函數會被放在子任務佇列而非立即執行，因此他會在當前的事件迴圈結束、佇列清空時執行，例如：

```js
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

wait().then(() => console.log(4));
Promise.resolve()
  .then(() => console.log(2))
  .then(() => console.log(3));
console.log(1); // 1, 2, 3, 4
```

看更多
---

* `Promise.then()`
* Promises/A+ 特色
* 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
* Nolan Lawson: We have a problem with promises — Common mistakes with promises


正規表達式 - JavaScript

正規表達式
=====

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

正規表達式是被用來匹配字串中字元組合的模式。在 JavaScript 中，正規表達式也是物件，這些模式在 `RegExp` (en-US) 的 `exec` (en-US) 和 `test` (en-US) 方法中，以及 `String` 的 `match` (en-US)、`replace` (en-US)、`search` (en-US)、`split` (en-US) 等方法中被運用。這一章節將解說 JavaScript 中的正規表達式。

建立正規表達式
-------

你可透過下列兩種方法去創建一條正規表達式：

使用正規表達式字面值（regular expression literal），包含兩個 `/` 字元之間的模式如下：

```js
var re = /ab+c/;
```

正規表達式字面值在 script 載入時會被編譯，當正規表達式為定值時，使用此方法可獲得較佳效能。

或呼叫 `RegExp` (en-US) 物件的建構函式，如下：

```js
var re = new RegExp("ab+c");
```

使用建構子函數供即時編譯正則表達式，當模式會異動、事先未知匹配模式、或者你將從其他地方取得時，使用建構子函數將較為合適。

撰寫正規表達模式
--------

正規表達模式由數個簡易字元組成，例如 `/abc/`，或是由簡易字元及特殊符號組合而成，例如 `/ab*c/`、`/Chapter (\d+)\.\d*/ )`。最後一個範例用到了括號，這在正規表達式中用作記憶組，使用括號的匹配將會被留到後面使用，在使用帶括號的配對子字串有更多解釋。

### 使用簡易模式

簡易的模式是有你找到的直接匹配所構成的。比如：`/abc/` 這個模式就匹配了在一個字符串中，僅僅字符 `'abc'` 同時出現並按照這個順序。這兩個句子中「*Hi, do you know your abc's?*」和「*The latest airplane designs evolved from slabcraft.*」就會匹配成功。在上面的兩個實例中，匹配的是子字符串 'abc'。在字符串中的 "Grab crab"('ab c') 中將不會被匹配，因為它不包含任何的 'abc' 字符串。

### 使用特殊字元

當你需要搜尋一個比直接匹配需要更多條件的匹配，比如搜尋一或多個 'b'，或者搜尋空格，那麼這個模式將要包含特殊字符。例如： 模式 `/ab*c/` 匹配了一個單獨的 'a' 後面跟了零或多個 'b'（\* 的意思是前面一項出現了零或多個），且後面跟著 'c' 的任何字符組合。在字符串 "cbbabbbbcdebc" 中，這個模式匹配了子字符串 "abbbbc"。

下面的表格列出了在正則表達式中可以利用的特殊字符完整列表以及描述。

| 字元 | 解說 |
| --- | --- |
| `\` | 反斜線放在非特殊符號前面，使非特殊符號不會被逐字譯出，代表特殊作用。 例如：'b' 如果沒有 '\' 在前頭，功能是找出小寫 b；若改為 '\b' 則代表的是邊界功能，block 用意。 /\bter\b/.test("interest") //false /\bter\b/.test("in ter est") //true `/a*/` 找出 0 個或是 1 個以上的 a；而 /a\\*/ 找出 'a\*' 這個字串 /aaaa\*/g.test("caaady") //true /a\\*/.test("caaady") //false '\' 也能使自身表現出來，表現 '\' ，必須以 '\\' 表達。 /[\\]/.test(">\\<") //true |
| `^` | 匹配輸入的開頭，如果 multiline flag 被設為 true，則會匹配換行字元後。例如：`/^A/` 不會匹配「an A」的 A，但會匹配「An E」中的 A。「`^`」出現在字元集模式的字首中有不同的意思，詳見補字元集。 |
| `$` | 匹配輸入的結尾，如果 multiline flag 被設為 true，則會匹配換行字元。例如：`/t$/` 不會匹配「eater」中的 t，卻會匹配「eat」中的 t。 |
| `*` | 匹配前一字元 0 至多次。 下面舉例要求基本 'aaaa' ，'a\*' 後面有 0 個或多個 a 的意思 /aaaaa\*/g.test("caaady") //false 例如：`/bo*/` 匹配「A ghost booooed」中的 boooo、「A bird warbled」中的 b，但在「A goat grunted」中不會匹配任何字串。 |
| `+` | 匹配前一字元 1 至多次，等同於 `{1,}`。例如：`/a+/` 匹配「candy」中的 a，以及所有「caaaaaaandy」中的 a。 |
| `?` | 匹配前一字元 0 至 1 次，等同於 `{0,1}`。例如：`/e?le?/` 匹配「angel」中的 el、「angle」中的 le、以及「oslo」中的 l。如果是使用在 `*`、`+`、`?` 或 `{}` 等 quantifier 之後，將會使這些 quantifier non-greedy（也就是儘可能匹配最少的字元），與此相對的是 greedy（匹配儘可能多的字元）。例如：在「123abc」中應用 `/\d+/` 可匹配「123」，但使用 `/\d+?/` 在相同字串上只能匹配「1」。 Also used in lookahead assertions, as described in the `x(?=y)` and `x(?!y)` entries of this table. |
| `.` | （小數點）匹配除了換行符號之外的單一字元。例如：/.n/ 匹配「nay, an apple is on the tree」中的 an 和 on，但在「nay」中沒有匹配。 |
| `(x)` | Capturing Parentheses 匹配 'x' 並記住此次的匹配，如下面的範例所示。在 正則表達示 /(foo) (bar) \1 \2/ 中的 (foo) 與 (bar) 可匹配了 "foo bar foo bar" 這段文字中的前兩個字，而 \1 與 \2 則匹配了後面的兩個字。注意， \1, \2, ..., \n 代表的就是前面的 pattern，以本範例來說，/(foo) (bar) \1 \2/ 等同於 /(foo) (bar) (foo) (bar)/。用於取代(replace)的話，則是用 $1, $2,...,$n。如 'bar boo'.replace(/(...) (...)/, '$2 $1'). `$&` 表示已匹配的字串 |
| `(?:x)` | *Non-Capturing Parentheses*找出 'x'，這動作不會記憶 `()`為 group 的意思，檢查時會再 wrap 一次，若有 `g` flag 會無效， `?:` 代表只要 group 就好，不要 wrap 有無 `()` 差別 ？ `'foo'.match(/(foo)/)` `// ['foo', 'foo', index: 0, input: 'foo' ] 'foo'.match(/foo/) // [ 'foo', index: 0, input: 'foo' ]`有無`?:`差別？ `'foo'.match(/(foo){1,2}/) // [ 'foo', 'foo', index: 0, input: 'foo' ] 'foo'.match(/(?:foo){1,2}/) [ 'foo', index: 0, input: 'foo' ]` 連`()`都沒有，則`{1,2}`只是適用在`foo`的第二個`o`的條件而已。更多資訊詳見 Using parentheses 。 |
| `x(?=y)` | 符合'x'，且後接的是'y'。'y'為'x'存在的意義。 例如：`/Jack(?=Sprat)/，`在後面是 Sprat 的存在下，Jack 才有意義。 `/Jack(?=Sprat|Frost)/`後面是 Sprat「或者是」Frost 的存在下，Jack 才有意義。但我們要找的目標是 Jack，後面的條件都只是 filter/條件的功能而已。 |
| `x(?!y)` | 符合'x'，且後接的不是'y'。'y'為否定'x'存在的意義，後面不行前功盡棄(negated lookahead)。 例如： `/\d+(?!\.)/` ，要找一個或多個數字時，在後面接的不是「點」的情況下成立。 `var result = /\d+(?!\.)/.exec("3.141")` ， result 執行出來為[ '141', index: 2, input: '3.141']， index:2，代表 141 從 index = 2 開始。 |
| `x|y` | 符合「x」或「y」。舉例來說，`/green|red/` 的話，會匹配 `"green apple"` 中的 `"green"` 以及 `"red apple."` 的 `"red"` 。 |
| `{n}` | 規定符號確切發生的次數，n 為正整數例如：`/a{2}/`無法在 "candy" 找到、但 "caandy" 行；若字串擁有 2 個以上 "caaandy" 還是只會認前面 2 個。 |
| `{n,m}` | 搜尋條件：n 為至少、m 為至多，其 n、m 皆為正整數。若把 m 設定為 0，則為 Invalid regular expression。例如：`/a{1,3}/`無法在 "cndy" 匹配到；而在 "candy" 中的第 1 個"a"符合；在 "caaaaaaandy" 中的前 3 個 "aaa" 符合，雖然此串有許多 a，但只認前面 3 個。 |
| `[xyz]` | 字元的集合。此格式會匹配中括號內所有字元, including escape sequences。特殊字元，例如點（`.`） 和米字號（`*`），在字元集合中不具特殊意義，所以不需轉換。若要設一個字元範圍的集合，可以使用橫線 `"-"` ，如下例所示： `[a-d]` 等同於 `[abcd]。`會匹配 "brisket" 的 "b" 、"city" 的 'c' ……等。 而`/[a-z.]+/` 和 `/[\w.]+/` 均可匹配字串 "test.i.ng" 。 |
| `[^xyz]` | bracket 中寫入的字元將被否定，匹配非出現在 bracket 中的符號。 可用 '-' 來界定字元的範圍。一般直接表達的符號都可以使用這種方式。`[^abc]`可以寫作[^`a-c]`. "brisket" 中找到 'r' 、"chop."中找到 'h'。 |
| `[\b]` | 吻合倒退字元 (U+0008). (不會跟 \b 混淆) |
| `\b` | 吻合文字邊界。A word boundary matches the position where a word character is not followed or preceded by another word-character. Note that a matched word boundary is not included in the match. In other words, the length of a matched word boundary is zero. (Not to be confused with `[\b]`.)Examples: `/\bm/` matches the 'm' in "moon" ; `/oo\b/` does not match the 'oo' in "moon", because 'oo' is followed by 'n' which is a word character; `/oon\b/` matches the 'oon' in "moon", because 'oon' is the end of the string, thus not followed by a word character; `/\w\b\w/` will never match anything, because a word character can never be followed by both a non-word and a word character.**Note:** JavaScript's regular expression engine defines a specific set of characters to be "word" characters. Any character not in that set is considered a word break. This set of characters is fairly limited: it consists solely of the Roman alphabet in both upper- and lower-case, decimal digits, and the underscore character. Accented characters, such as "é" or "ü" are, unfortunately, treated as word breaks. |
| `\B` | 吻合非文字邊界。This matches a position where the previous and next character are of the same type: Either both must be words, or both must be non-words. The beginning and end of a string are considered non-words.For example, `/\B../` matches 'oo' in "noonday", and `/y\B./` matches 'ye' in "possibly yesterday." |
| `\cX` | Where *X* is a character ranging from A to Z. Matches a control character in a string.For example, `/\cM/` matches control-M (U+000D) in a string. |
| `\d` | 吻合數字，寫法等同於 `[0-9] 。`例如：`/\d/` 或 `/[0-9]/` 在 "B2 is the suite number." 中找到 '2' |
| `\D` | 吻合非數字，寫法等同於 `[^0-9]。`例如：`/\D/` 或`/[^0-9]/` 在 "B2 is the suite number." 中找到 'B' 。 |
| `\f` | Matches a form feed (U+000C). |
| `\n` | Matches a line feed (U+000A). |
| `\r` | Matches a carriage return (U+000D). |
| `\s` | Matches a single white space character, including space, tab, form feed, line feed. Equivalent to `[ \f\n\r\t\v\u00a0\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]`.For example, `/\s\w*/` matches ' bar' in "foo bar." |
| `\S` | Matches a single character other than white space. Equivalent to `[^ \f\n\r\t\v\u00a0\\u1680u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000]`.For example, `/\S\w*/` matches 'foo' in "foo bar." |
| `\t` | Matches a tab (U+0009). |
| `\v` | Matches a vertical tab (U+000B). |
| `\w` | 包含數字字母與底線，等同於`[A-Za-z0-9_]`。例如： `/\w/` 符合 'apple'中的 'a' 、'$5.28 中的 '5' 以及 '3D' 中的 '3'。For example, `/\w/` matches 'a' in "apple," '5' in "$5.28," and '3' in "3D." |
| `\W` | 包含"非"數字字母與底線，等同於`[^A-Za-z0-9_]`。例如： `/\W/` 或是 `/[^A-Za-z0-9_]/` 符合 "50%." 中的 '%'For example, `/\W/` or `/[^A-Za-z0-9_]/` matches '%' in "50%." |
| `\n` | 其中 *n* 是一個正整數，表示第 *n* 個括號中的子字串匹配（包含括號中的所有的字串匹配）例如： `/apple(,)\sorange\1/` 符合 "apple, orange, cherry, peach." 的 'apple, orange,' 。（ `\1` 表示第一個 partten ，也就是 `(,)`）For example, `/apple(,)\sorange\1/` matches 'apple, orange,' in "apple, orange, cherry, peach." |
| `\0` | Matches a NULL (U+0000) character. Do not follow this with another digit, because `\0<digits>` is an octal escape sequence. Instead use `\x00`. |
| `\xhh` | Matches the character with the code hh (two hexadecimal digits) |
| `\uhhhh` | Matches the character with the code hhhh (four hexadecimal digits). |

Escaping user input that is to be treated as a literal string within a regular expression—that would otherwise be mistaken for a special character—can be accomplished by simple replacement:

```js
function escapeRegExp(string) {
  return string.replace(/[.\*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
}
```

The g after the regular expression is an option or flag that performs a global search, looking in the whole string and returning all matches. It is explained in detail below in Advanced Searching With Flags.

### 使用括號

Parentheses around any part of the regular expression pattern causes that part of the matched substring to be remembered. Once remembered, the substring can be recalled for other use, as described in Using Parenthesized Substring Matches.

For example, the pattern `/Chapter (\d+)\.\d*/` illustrates additional escaped and special characters and indicates that part of the pattern should be remembered. It matches precisely the characters 'Chapter ' followed by one or more numeric characters (`\d` means any numeric character and `+` means 1 or more times), followed by a decimal point (which in itself is a special character; preceding the decimal point with \ means the pattern must look for the literal character '.'), followed by any numeric character 0 or more times (`\d` means numeric character, `*` means 0 or more times). In addition, parentheses are used to remember the first matched numeric characters.

This pattern is found in "Open Chapter 4.3, paragraph 6" and '4' is remembered. The pattern is not found in "Chapter 3 and 4", because that string does not have a period after the '3'.

To match a substring without causing the matched part to be remembered, within the parentheses preface the pattern with `?:`. For example, `(?:\d+)` matches one or more numeric characters but does not remember the matched characters.

運用正規表達式
-------

Regular expressions are used with the `RegExp` methods `test` and `exec` and with the `String` methods `match`, `replace`, `search`, and `split`. These methods are explained in detail in the JavaScript reference.

| Method | Description |
| --- | --- |
| `exec` (en-US) | A `RegExp` method that executes a search for a match in a string. It returns an array of information or null on a mismatch. |
| `test` (en-US) | A `RegExp` method that tests for a match in a string. It returns true or false. |
| `match` (en-US) | A `String` method that executes a search for a match in a string. It returns an array of information or null on a mismatch. |
| `search` (en-US) | A `String` method that tests for a match in a string. It returns the index of the match, or -1 if the search fails. |
| `replace` (en-US) | A `String` method that executes a search for a match in a string, and replaces the matched substring with a replacement substring. |
| `split` (en-US) | A `String` method that uses a regular expression or a fixed string to break a string into an array of substrings. |

When you want to know whether a pattern is found in a string, use the `test` or `search` method; for more information (but slower execution) use the `exec` or `match` methods. If you use `exec` or `match` and if the match succeeds, these methods return an array and update properties of the associated regular expression object and also of the predefined regular expression object, `RegExp`. If the match fails, the `exec` method returns `null` (which coerces to `false`).

In the following example, the script uses the `exec` method to find a match in a string.

```js
var myRe = /d(b+)d/g;
var myArray = myRe.exec("cdbbdbsbz");
```

If you do not need to access the properties of the regular expression, an alternative way of creating `myArray` is with this script:

```js
var myArray = /d(b+)d/g.exec("cdbbdbsbz"); // similar to "cdbbdbsbz".match(/d(b+)d/g); however,
// the latter outputs Array [ "dbbd" ], while
// /d(b+)d/g.exec('cdbbdbsbz') outputs Array [ "dbbd", "bb" ].
// See below for further info (CTRL+F "The behavior associated with the".)
```

If you want to construct the regular expression from a string, yet another alternative is this script:

```js
var myRe = new RegExp("d(b+)d", "g");
var myArray = myRe.exec("cdbbdbsbz");
```

With these scripts, the match succeeds and returns the array and updates the properties shown in the following table.

Results of regular expression execution.| 物件 | Property or index | 說明 | 範例 |
| --- | --- | --- | --- |
| `myArray` |  | The matched string and all remembered substrings. | `['dbbd', 'bb', index: 1, input: 'cdbbdbsbz']` |
| `index` | The 0-based index of the match in the input string. | `1` |
| `input` | The original string. | `"cdbbdbsbz"` |
| `[0]` | The last matched characters. | `"dbbd"` |
| `myRe` | `lastIndex` |
 The index at which to start the next match. (This property is set only
 if the regular expression uses the g option, described in
 Advanced Searching With Flags.)
  | `5` |
| `source` |
 The text of the pattern. Updated at the time that the regular expression
 is created, not executed.
  | `"d(b+)d"` |

As shown in the second form of this example, you can use a regular expression created with an object initializer without assigning it to a variable. If you do, however, every occurrence is a new regular expression. For this reason, if you use this form without assigning it to a variable, you cannot subsequently access the properties of that regular expression. For example, assume you have this script:

```js
var myRe = /d(b+)d/g;
var myArray = myRe.exec("cdbbdbsbz");
console.log("The value of lastIndex is " + myRe.lastIndex);

// "The value of lastIndex is 5"
```

However, if you have this script:

```js
var myArray = /d(b+)d/g.exec("cdbbdbsbz");
console.log("The value of lastIndex is " + /d(b+)d/g.lastIndex);

// "The value of lastIndex is 0"
```

The occurrences of `/d(b+)d/g` in the two statements are different regular expression objects and hence have different values for their `lastIndex` property. If you need to access the properties of a regular expression created with an object initializer, you should first assign it to a variable.

### Using Parenthesized Substring Matches

Including parentheses in a regular expression pattern causes the corresponding submatch to be remembered. For example, `/a(b)c/` matches the characters 'abc' and remembers 'b'. To recall these parenthesized substring matches, use the `Array` elements `[1]`, ..., `[n]`.

The number of possible parenthesized substrings is unlimited. The returned array holds all that were found. The following examples illustrate how to use parenthesized substring matches.

下面這個 script 以 `replace()` (en-US) 方法移轉字串位置。對於要被置換的文字內容，以 `$1` 和 `$2` 來代表先前 re 這個變數裡面，找出來綑綁且照順序來表示兩個子字串。

```js
var re = /(\w+)\s(\w+)/;
var str = "John Smith";
var newstr = str.replace(re, "$2, $1");
console.log(newstr);

// "Smith, John"
```

### Advanced Searching With Flags

Regular expressions have five optional flags that allow for global and case insensitive searching. These flags can be used separately or together in any order, and are included as part of the regular expression.

| Flag | Description |
| --- | --- |
| `g` | Global search. |
| i | Case-insensitive search. |
| m | Multi-line search. |
| u | unicode; treat a pattern as a sequence of unicode code points |
| y | Perform a "sticky" search that matches starting at the current position in the target string. See `sticky` (en-US) |

To include a flag with the regular expression, use this syntax:

```js
var re = /pattern/flags;
```

or

```js
var re = new RegExp("pattern", "flags");
```

Note that the flags are an integral part of a regular expression. They cannot be added or removed later.

For example, `re = /\w+\s/g` creates a regular expression that looks for one or more characters followed by a space, and it looks for this combination throughout the string.

```js
var re = /\w+\s/g;
var str = "fee fi fo fum";
var myArray = str.match(re);
console.log(myArray);

// ["fee ", "fi ", "fo "]
```

You could replace the line:

```js
var re = /\w+\s/g;
```

with:

```js
var re = new RegExp("\\w+\\s", "g");
```

and get the same result.

The behavior associated with the '**`g`**' flag is different when the `.exec()` method is used. (The roles of "class" and "argument" get reversed: In the case of `.match()`, the string class (or data type) owns the method and the regular expression is just an argument, while in the case of `.exec()`, it is the regular expression that owns the method, with the string being the argument. Contrast *`str.match(re)`* versus *`re.exec(str)`*.) The '**`g`**' flag is used with the **`.exec()`** method to get iterative progression.

```js
var xArray;
while ((xArray = re.exec(str))) console.log(xArray);
// produces:
// ["fee ", index: 0, input: "fee fi fo fum"]
// ["fi ", index: 4, input: "fee fi fo fum"]
// ["fo ", index: 7, input: "fee fi fo fum"]
```

The `m` flag is used to specify that a multiline input string should be treated as multiple lines. If the `m` flag is used, `^` and `$` match at the start or end of any line within the input string instead of the start or end of the entire string.

範例
--

The following examples show some uses of regular expressions.

### Changing the order in an input string

The following example illustrates the formation of regular expressions and the use of `string.split()` and `string.replace()`. It cleans a roughly formatted input string containing names (first name last) separated by blanks, tabs and exactly one semicolon. Finally, it reverses the name order (last name first) and sorts the list.

```js
// The name string contains multiple spaces and tabs,
// and may have multiple spaces between first and last names.
var names = "Orange Trump ;Fred Barney; Helen Rigby ; Bill Abel ; Chris Hand ";

var output = ["---------- Original String\n", names + "\n"];

// Prepare two regular expression patterns and array storage.
// Split the string into array elements.

// pattern: possible white space then semicolon then possible white space
var pattern = /\s\*;\s\*/;

// Break the string into pieces separated by the pattern above and
// store the pieces in an array called nameList
var nameList = names.split(pattern);

// new pattern: one or more characters then spaces then characters.
// Use parentheses to "memorize" portions of the pattern.
// The memorized portions are referred to later.
pattern = /(\w+)\s+(\w+)/;

// Below is the new array for holding names being processed.
var bySurnameList = [];

// Display the name array and populate the new array
// with comma-separated names, last first.
//
// The replace method removes anything matching the pattern
// and replaces it with the memorized string—the second memorized portion
// followed by a comma, a space and the first memorized portion.
//
// The variables $1 and $2 refer to the portions
// memorized while matching the pattern.

output.push("---------- After Split by Regular Expression");

var i, len;
for (i = 0, len = nameList.length; i < len; i++) {
  output.push(nameList[i]);
  bySurnameList[i] = nameList[i].replace(pattern, "$2, $1");
}

// Display the new array.
output.push("---------- Names Reversed");
for (i = 0, len = bySurnameList.length; i < len; i++) {
  output.push(bySurnameList[i]);
}

// Sort by last name, then display the sorted array.
bySurnameList.sort();
output.push("---------- Sorted");
for (i = 0, len = bySurnameList.length; i < len; i++) {
  output.push(bySurnameList[i]);
}

output.push("---------- End");

console.log(output.join("\n"));
```

### 使用特殊字元驗證輸入

In the following example, the user is expected to enter a phone number. When the user presses the "Check" button, the script checks the validity of the number. If the number is valid (matches the character sequence specified by the regular expression), the script shows a message thanking the user and confirming the number. If the number is invalid, the script informs the user that the phone number is not valid.

Within non-capturing parentheses `(?:` , the regular expression looks for three numeric characters `\d{3}` OR `|` a left parenthesis `\(` followed by three digits `\d{3}`, followed by a close parenthesis `\)`, (end non-capturing parenthesis `)`), followed by one dash, forward slash, or decimal point and when found, remember the character `([-\/\.])`, followed by three digits `\d{3}`, followed by the remembered match of a dash, forward slash, or decimal point `\1`, followed by four digits `\d{4}`.

The `Change` event activated when the user presses Enter sets the value of `RegExp.input`.

```html
<!doctype html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
    <meta http-equiv="Content-Script-Type" content="text/javascript" />
    <script type="text/javascript">
 var re = /(?:\d{3}|\(\d{3}\))([-\/\.])\d{3}\1\d{4}/;
 function testInfo(phoneInput) {
 var OK = re.exec(phoneInput.value);
 if (!OK)
 window.alert(
 phoneInput.value + " isn't a phone number with area code!",
 );
 else window.alert("Thanks, your phone number is " + OK[0]);
 }
 </script>
  </head>
  <body>
    <p>
      Enter your phone number (with area code) and then click "Check". <br />The
      expected format is like ###-###-####.
    </p>
    <form action="#">
      <input id="phone" /><button
 onclick="testInfo(document.getElementById('phone'));">
        Check
      </button>
    </form>
  </body>
</html>
```

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


語法與型別 - JavaScript

語法與型別
=====

* « 前頁
* 次頁 »

本章討論 JavaScript 的基本語法與基礎資料類型、包括變數、常數、字元常數

基礎知識
----

JavaScript 許多基本語法借鑒自 Java，C 或是 C++，但亦受 Awk、Perl 和 Python 的影響。

JavaScript 是 Case-sensitive（區分大小寫）並使用 Unicode 編碼。舉例來說，Früh (德文的"early") 可以當作變數的名稱。
```

var Früh = "foobar";
```

但變數 früh 並不等於 Früh，因為大小寫對 JavaScript 是有區別的。

在 JavaScript 中，每行指令被稱為 Statements (en-US)，並用分號（;）分隔。空格、Tab 與換行符號皆被視為空白。JavaScript 的文件會從左到右進行掃描，並轉換成一系列的元素，像是令牌（Token）、控制字符（Control characters）、換行器（line terminators）、註解（Comments）或是空白（Withespace），ECMAScript 也定義了特定的保留字和字面值，並在每個沒有加分號的 Statement 自動加上分號。然而，推薦的作法還是在每個 Statement 的結尾自行加上分號，以防止一些潛在的副作用，如果需要更多資訊，可以參考這篇。

註解（Comments）
------------

註解語法跟 C++ 和其他語言相同：

```js
// a one line comment

/\* this is a longer,
 multi-line comment
 \*/

/\* You can't, however, /\* nest comments \*/ SyntaxError \*/
```

宣告（Declarations）
----------------

JavaScript 有三種宣告方式

`var`

宣告一個可隨意更改其內容的變數

`let`

宣告一個可隨意更改其內容的區塊區域變數

`const`

宣告一個只可讀取的不可變常數

### 變數（Variables）

變數（variable）是對值（value）的引用，變數的名稱被稱為 identifiers 需要遵從一定的規則。

在 JavaScript 中，變數必須使用字母（letter）、下底線（\_）、錢號（$）作為開頭；後面的字員組成可以包含數字（0-9）。JavaScript 是區分大小寫（case sensitive）的，大寫字母（'A' ~ 'Z'）和小寫字母（'a' ~ 'z'）皆可使用且不相等。

You can use most of ISO 8859-1 or Unicode letters such as å and ü in identifiers (for more details see this blog post). You can also use the Unicode escape sequences as characters in identifiers.

Some examples of legal names are `Number_hits`, `temp99`, `$credit`, and `_name`.

### 定義變數

你可以透過三種方式來定義變數：

* 透過保留字 `var` 來定義變數，舉例來說： `var x = 42`，這種方式可以用來定義區域以及全域變數。
* 直接指定一個值給該變數，例如：`x = 42`，這種方式只能定義全域變數，如果在方法外面使用該方法定義變數，嚴格模式裡會產生警告，該定義方式應該盡可能避免。
* 透過保留字 `let`，舉例來說：`let y = 13`，`let` 可以用來定義區塊裡的區域變數。想瞭解更多，可以參考變數區域的章節。
### 變數取值

變數可以透過 `var` 或是 `let` 來定義，如果尚未指定數值給該變數，那麼該變數的值會是 `undefined` (en-US)。如果嘗試去存取未定義的變數，會跳出 `ReferenceError` (en-US) 的例外。

```js
var a;
console.log("The value of a is " + a); // The value of a is undefined

console.log("The value of b is " + b); // The value of b is undefined
var b;

console.log("The value of c is " + c); // Uncaught ReferenceError: c is not defined

let x;
console.log("The value of x is " + x); // The value of x is undefined

console.log("The value of y is " + y); // Uncaught ReferenceError: y is not defined
let y;
```

你可以利用 `undefined` 來判斷該變數是否有值，在下面的程式碼的例子中，`input` 這個變數沒有賦值，`if` 判斷式會得到 `true` 的結果。

```js
var input;
if (input === undefined) {
  doThis();
} else {
  doThat();
}
```

被賦予 `undefined` 的變數，在被當做布林值的情境下都會被視為 `false`，以下面的例子來說，程式碼會執行 `myFunction`，因為 `myArray` 是 `undefined`：

```js
var myArray = [];
if (!myArray[0]) myFunction();
```

被賦予 `undefined` 的變數，在和數值進行運算之後，會被轉成非數值（`NaN`）：

```js
var a;
a + 2; // Evaluates to NaN
```

當你對 `null` (en-US) 進行運算，`null` (en-US) 會自動轉換成數值 0，如果當做布林值運算，會被當成 `false`，舉例來說：

```js
var n = null;
console.log(n \* 32); // Will log 0 to the console
```

### 變數範圍

當我們在函式外宣告一個變數時，這個變數會是一個全域變數 (global variable), 因為在這份程式文件裡面的所有程式碼都可以使用到這個變數。但當我們只在函式內宣告變數時，這變數是區域變數 (local variable)，因為變數只會在函式內被使用到。

**請注意!!** 在 ECMAScript 2015 以前的 JavaScript 版本裡，並沒有定義區塊描述 (block statement) 的變數有效範圍。更精確的說，之前版本所定義的變數，其特性相當於全域變數；不只在宣告的區塊或函數裡面有效 ，其變數值還會超出宣告區塊而影響到全部的描述碼。

從下面例子來看，其輸出結果會是 5。雖然 x 是在 if { } 區塊裡面被宣告的，但卻因為有全域變數的特性，因此溢出大括號而成為後續描述碼的變數值。

```js
if (true) {
  var x = 5;
}
console.log(x); // x is 5
```

接著舉一個 ECMAScript 2015 之後的宣告範例。當使用了 `let` 這個區域變數宣告方式，變數 y 的有效範圍只有在 if { } 的範圍內，因此輸出結果是 ReferenceError。

```js
if (true) {
  let y = 5;
}
console.log(y); // ReferenceError: y is not defined (y沒有被定義)
```

### 變數提升

 在 JavaScript 中另一件有關變數不常見的事, 是你可引用一個較晚宣告的變數並且不會有異常。這個概念被稱為「**提升**（**hoisting**）」；從意義上來說明，變數在 JavaScript 中是「被提升（hoisted）」或「被抬至（lifted）」到函式（function）或陳述式（statement）的頂部。
 然而，被提升（hoisted）的變數將返回一個未定義的值（undefined）。所以即使你在使用或者引用這個變數之後才宣告和初始化它，它仍然會返回它是一個未定義的值（undefined）。

```js
/\*\*
 \* Example 1
 \*/
console.log(x === undefined); // true
var x = 3;

/\*\*
 \* Example 2
 \*/
// will return a value of undefined
var myvar = "my value";

(function () {
  console.log(myvar); // undefined
  var myvar = "local value";
})();
```

上面的例子可以轉譯成如下相同的程式:

```js
/\*\*
 \* Example 1
 \*/
var x;
console.log(x === undefined); // true
x = 3;

/\*\*
 \* Example 2
 \*/
var myvar = "my value";

(function () {
  var myvar;
  console.log(myvar); // undefined
  myvar = "local value";
})();
```

由於提升(hoisting)，全部在函數(function) 中的 var 陳述式應該盡可能地置放在接近函數(function)的頂部。這個最佳實踐增加了程式碼的清晰度。

在 ECMAScript 2015 中，let（const）不會將變數提升到區塊（block）的頂部。但是，在變數宣告之前就引用塊中的變數，會導致 `ReferenceError` (en-US)。變數從區塊（block）的開始到宣告被處理之前，就處於「暫時無效（temporal dead zone）」。

```js
console.log(x); // ReferenceError
let x = 3;
```

### 函式提升

針對函式來說，只有函式宣告式(function declaration)提昇到頂部，但函式表示式(function exprssion) 不被提昇至頂部。

```js
/\* Function declaration \*/

foo(); // "bar"

function foo() {
  console.log("bar");
}

/\* Function expression \*/

baz(); // TypeError: baz is not a function

var baz = function () {
  console.log("bar2");
};
```

### 全域變數 (Global variables)

全域變數事實上是全域物件的屬性值。在網頁中的全域物件是 `window` (en-US)，因此你可使用 `window.variable` 的語法來設定及存取全域變數。

Consequently, 你可以指定 window 或 frame 物件的名稱來存取在另一個在 window 物件或 frame 物件所宣告的全域變數。例如，如果在一個文檔中已宣告一個稱為 `phoneNumber` 的變數，你可以在 iframe 中使用 `parent.phoneNumber` 來存取該變數

### 常數 (Constants)

你可用 `const` 關鍵字來建立一個唯讀、有名稱的常數。 常數識別子的命名語法與變數識別子的命名語法是一樣的: 必須由一個英文字母，底線或錢符號($)開始，之後可包含英文字母，數字及底線字元。

```js
const PI = 3.14;
```

當程式執行時，無法再透過賦值或重新宣告來改變常數已設定的值。常數必須被初始化。

The scope rules for constants are the same as those for `let` block-scope variables. If the `const` keyword is omitted, the identifier is assumed to represent a variable.

你不能在同一個局部範圍內使用與其它函式或變數相同的名稱來宣告變數。例如:

```js
// THIS WILL CAUSE AN ERROR
function f() {}
const f = 5;

// THIS WILL CAUSE AN ERROR ALSO
function f() {
  const g = 5;
  var g;

  //statements
}
```

但是常數物件內的物件屬性並不受到保護，因此以下陳述式可以正常執行。

```js
const MY\_OBJECT = { key: "value" };
MY\_OBJECT.key = "otherValue";
```

資料結構及型別
-------

### 資料型別 (Data types)

最新 ECMAScript 標準定義以下七種資料型別:

* 六種基本(primitives (en-US))資料型別 :
    + Boolean. `true` and `false`.
    + null. A special keyword denoting a null value. Because JavaScript is case-sensitive, `null` is not the same as `Null`, `NULL`, or any other variant.
    + undefined (en-US). A top-level property whose value is undefined.
    + Number. `42` or `3.14159`.
    + String (en-US). "Howdy"
    + Symbol (en-US) (new in ECMAScript 2015). A data type whose instances are unique and immutable.
* and Object

儘管這些變數關聯性很小，他們可以讓你在你的應用程式中，產生出有意義的函數。

物件 (en-US)與 函數 (en-US) 在語言中是其它的基本元素. 你可以把物件想成是一個被命名過且用來裝數值的容器,以及函數則為你的應用程式所執行的步驟.

### 資料型別轉換

JavaScript 是一個動態型別的語言，這意味著你不需要在宣告變數時定義它的資料型別，程式執行時會自動轉換，你可以用下面方式宣告變數：

```js
var answer = 42;
```

你可以指派字串在同個變數中，例如：

```js
answer = "Thanks for all the fish...";
```

由於 Javascript 是一個動態型別的語言，因此這樣的宣告方式不會導致錯誤。

在該陳述式中，它調用了字串和數字，並使用 + 進行運算，JavaScript 會自動把數字轉換成字串，例如：

```js
x = "The answer is " + 42; // "The answer is 42"
y = 42 + " is the answer"; // "42 is the answer"
```

在該陳述式中，它調用了其它運算子，JavaScript 就不會將數字轉換成字串，例如：

```js
"37" - 7; // 30
"37" + 7; // "377"
```

### 字串轉數值

當代表數字的值以字串形式存在記憶體中，有些方法可用來將這種字串轉換成整數或浮點數。

* `parseInt()`
* `parseFloat()` (en-US)

`parseInt` 只會返回整數，因此減少了對小數的使用。此外，parseInt 的最佳實務是始終包含基數參數。基數參數用於指定使用的數值系統。

另一個將字串轉成數字是使用單元 `+` (unary plus) 運算子:

```js
'1.1' + '1.1' = '1.11.1'
(+'1.1') + (+'1.1') = 2.2
// 注意: 括號是為了易於閱讀，並不是必須的.
```

字面值（Literals）
-------------

你能使用字面值來表示 JavaScript 中的值。這些是你在腳本中實際提供的固定值，而不是變量。本節描述以下類型的字面值：

* Array literals
* Boolean literals
* Floating-point literals
* Integers
* Object literals
* RegExp literals
* String literals
### 陣列字面值 (Array literals)

陣列字面值是零或多個表達式的列表，每個表達式代表一個數組元素，並用方括號（[]）括起來。使用陣列字面值創建陣列時，將使用指定的值作為其元素對其進行初始化，並將其長度設置為指定的參數值。

以下範例創建了陣列 `coffees` ，長度為 3 並包含三個元素：

```js
var coffees = ["French Roast", "Colombian", "Kona"];
```

**備註：** An array literal is a type of object initializer. See Using Object Initializers (en-US).

If an array is created using a literal in a top-level script, JavaScript interprets the array each time it evaluates the expression containing the array literal. In addition, a literal used in a function is created each time the function is called.

Array literals are also `Array` objects. See `Array` and Indexed collections (en-US) for details on `Array` objects.

#### Extra commas in array literals

You do not have to specify all elements in an array literal. If you put two commas in a row, the array is created with `undefined` for the unspecified elements. The following example creates the `fish` array:

```js
var fish = ["Lion", , "Angel"];
```

This array has two elements with values and one empty element (`fish[0]` is "Lion", `fish[1]` is `undefined`, and `fish[2]` is "Angel").

If you include a trailing comma at the end of the list of elements, the comma is ignored. In the following example, the length of the array is three. There is no `myList[3]`. All other commas in the list indicate a new element.

**備註：** Trailing commas can create errors in older browser versions and it is a best practice to remove them.

```js
var myList = ["home", , "school"];
```

In the following example, the length of the array is four, and `myList[0]` and `myList[2]` are missing.

```js
var myList = [, "home", , "school"];
```

In the following example, the length of the array is four, and `myList[1]` and `myList[3]` are missing. **Only the last comma is ignored.**

```js
var myList = ["home", , "school", ,];
```

Understanding the behavior of extra commas is important to understanding JavaScript as a language, however when writing your own code: explicitly declaring the missing elements as `undefined` will increase your code's clarity and maintainability.

### 布林字面值 (Boolean literals)

布林型別有兩種字面值： `true` 跟 `false`.

Do not confuse the primitive Boolean values `true` and `false` with the true and false values of the Boolean object. The Boolean object is a wrapper around the primitive Boolean data type. See `Boolean` for more information.

### 整數字面值 (Numerical literals)

整數能表示為「十進制」、「十六進制」、「八進制」、「二進制」

* 十進制整數字面值由「『不帶前導 0』的整數序列」組成
* 八進制整數字面值由「『前導 0』」或『前導 0o』或『前導 0O』的整數序列」組成。八進制整數只能包含數字 0-7
* 十六進制整數字面值由「『前導 0x』」或『前導 0X』的整數序列」組成。十六進制整數只能包含數字 0-9 、字母 A-F 和 a-f
* 二進制整數字面值由「『前導 0b』」或『前導 0B』的整數序列」組成。二進制整數只能包含數字 0 跟 1

整數字面值範例如下：
```

0, 117 and -345 (decimal, base 10)
015, 0001 and -0o77 (octal, base 8)
0x1123, 0x00111 and -0xF1A7 (hexadecimal, "hex" or base 16)
0b11, 0b0011 and -0b11 (binary, base 2)
```

更多資訊請參閱 Numeric literals in the Lexical grammar reference.

### 浮點數字面值 (Floating-point literals)

浮點數字面值能包含以下部分：

* 整數部分 (十進位，可帶符號 "+" 或 "-" 於整數前)
* 小數點 "."
* 小數部分 (另一個十進位整數)
* 指數部分

指數部分由「"e" 或 "E" 後面跟整數」所組成，可帶符號 "+" 或 "-" 於整數前。浮點數字面值至少由「一位數字」與「一個小數點 "e" (或 "E")」組成。

簡言之，於法如下：
```

[(+|-)][digits][.digits][(E|e)[(+|-)]digits]
```

舉個例子：
```

3.1415926
-.123456789
-3.1E+12
.1e-23
```

### 物件字面值 (Object literals)

物件字面值是用大括號（{}）括起來的零或多對鍵值對的列表。因為 "{" 將被解譯為區塊(block)的開頭，因此你不應在陳述句開頭使用物件字面值，這將導致錯誤或不預期的行為。

以下是物件字面值的範例。`car` 物件包含三個屬性：

* 第一個屬性 `myCar` 賦值為字串 '`Saturn`'
* 第二個屬性 `getCar` 賦值為「調用函數`carTypes('Honda')`」的結果
* 第三個屬性 `special` 使用現有變量 `sales` 賦值

```js
var sales = "Toyota";

function carTypes(name) {
  if (name === "Honda") {
    return name;
  } else {
    return "Sorry, we don't sell " + name + ".";
  }
}

var car = { myCar: "Saturn", getCar: carTypes("Honda"), special: sales };

console.log(car.myCar); // Saturn
console.log(car.getCar); // Honda
console.log(car.special); // Toyota
```

此外，你可以使用數字或字串字面值作為屬性名，也可將物件嵌套在另一個物件中。如下範例：

```js
var car = { manyCars: { a: "Saab", b: "Jeep" }, 7: "Mazda" };

console.log(car.manyCars.b); // Jeep
console.log(car[7]); // Mazda
```

物件屬性名可以是任何字串，包括空字串。如果屬性名不是有效的 JavaScript 識別字 或數字，則必須將其用引號引起來。無效的屬性名稱也不能作為點 (`.`) 屬性訪問，但是可以使用類似數組的符號（"`[]`"）進行訪問和設置。

```js
var unusualPropertyNames = {
  '': 'An empty string',
  '!': 'Bang!'
}
console.log(unusualPropertyNames.'');   // SyntaxError: Unexpected string
console.log(unusualPropertyNames['']);  // An empty string
console.log(unusualPropertyNames.!);    // SyntaxError: Unexpected token !
console.log(unusualPropertyNames['!']); // Bang!
```

#### Enhanced Object literals

In ES2015, object literals are extended to support setting the prototype at construction, shorthand for `foo: foo` assignments, defining methods, making super calls, and computing property names with expressions. Together, these also bring object literals and class declarations closer together, and let object-based design benefit from some of the same conveniences.

```js
var obj = {
  // \_\_proto\_\_
  \_\_proto\_\_: theProtoObj,
  // Shorthand for ‘handler: handler’
  handler,
  // Methods
  toString() {
    // Super calls
    return "d " + super.toString();
  },
  // Computed (dynamic) property names
  ["prop\_" + (() => 42)()]: 42,
};
```

Please note:

```js
var foo = { a: "alpha", 2: "two" };
console.log(foo.a); // alpha
console.log(foo[2]); // two
//console.log(foo.2); // Error: missing ) after argument list
//console.log(foo[a]); // Error: a is not defined
console.log(foo["a"]); // alpha
console.log(foo["2"]); // two
```

### 正規表達式字面值 (RegExp literals)

正則表達式字面值是包含在斜杠間的樣式。以下是正則表達式文字的範例。

```js
var re = /ab+c/;
```

### 字串字面值 (String literals)

字串字面值是用雙引號（"）或單引號（'）包住的零或多個字元。字串必須用同類的引號定界；也就是「兩個單引號」或「兩個雙引號」。以下是字串字面值的範例：

```js
"foo";
"bar";
"1234";
"one line \n another line";
"John's cat";
```

你可以在字串字面值上調用 String 物件的任何方法 - JavaScript 將自動轉換字串字面值為臨時 String 物件並調用該方法，然後丟棄該臨時 String 物件。你還可以將 String.length 屬性與字串字面值一起使用：

```js
console.log("John's cat".length);
// Will print the number of symbols in the string including whitespace.
// In this case, 10.
```

In ES2015, template literals are also available. Template literals are enclosed by the back-tick (` `) (grave accent) character instead of double or single quotes. Template strings provide syntactic sugar for constructing strings. This is similar to string interpolation features in Perl, Python and more. Optionally, a tag can be added to allow the string construction to be customized, avoiding injection attacks or constructing higher level data structures from string contents.

```js
// Basic literal string creation
`In JavaScript '\n' is a line-feed.` // Multiline strings
`In JavaScript template strings can run
 over multiple lines, but double and single
 quoted strings cannot.`;

// String interpolation
var name = "Bob",
  time = "today";
`Hello ${name}, how are you ${time}?`;

// Construct an HTTP request prefix is used to interpret the replacements and construction
POST`http://foo.org/bar?a=${a}&b=${b}
 Content-Type: application/json
 X-Credentials: ${credentials}
 { "foo": ${foo},
 "bar": ${bar}}`(myOnReadyStateChangeHandler);
```

You should use string literals unless you specifically need to use a String object. See `String` for details on `String` objects.

#### 字串裡的特殊字元

除了普通字元，字串也能包含特殊字元，範例如下：

```js
"one line \n another line";
```

下表列出了可以在 JavaScript 字串中使用的特殊字元。

| 字元 | 意涵 |
| --- | --- |
| `\0` | Null Byte |
| `\b` | 退格 (Backspace) |
| `\f` | Form feed |
| `\n` | 換行 (New line) |
| `\r` | 回車 (Carriage return) |
| `\t` | 跳格 (Tab) |
| `\v` | Vertical tab |
| `\'` | Apostrophe or single quote |
| `\"` | Double quote |
| `\\` | Backslash character |
| `\XXX` | The character with the Latin-1 encoding specified by up to three octal digits *XXX* between 0 and 377. For example, \251 is the octal sequence for the copyright symbol. |
|  |  |
| `\xXX` | The character with the Latin-1 encoding specified by the two hexadecimal digits *XX* between 00 and FF. For example, \xA9 is the hexadecimal sequence for the copyright symbol. |
|  |  |
| `\uXXXX` | The Unicode character specified by the four hexadecimal digits *XXXX*. For example, \u00A9 is the Unicode sequence for the copyright symbol. See Unicode escape sequences. |
| `\u{XXXXX}` | Unicode code point escapes. For example, \u{2F804} is the same as the simple Unicode escapes \uD87E\uDC04. |

#### Escaping characters

For characters not listed in the table, a preceding backslash is ignored, but this usage is deprecated and should be avoided.

You can insert a quotation mark inside a string by preceding it with a backslash. This is known as *escaping* the quotation mark. For example:

```js
var quote = 'He read "The Cremation of Sam McGee" by R.W. Service.';
console.log(quote);
```

The result of this would be:
```

He read "The Cremation of Sam McGee" by R.W. Service.
```

To include a literal backslash inside a string, you must escape the backslash character. For example, to assign the file path `c:\temp` to a string, use the following:

```js
var home = "c:\\temp";
```

You can also escape line breaks by preceding them with backslash. The backslash and line break are both removed from the value of the string.

```js
var str =
  "this string \
is broken \
across multiple \
lines.";
console.log(str); // this string is broken across multiplelines.
```

Although JavaScript does not have "heredoc" syntax, you can get close by adding a line break escape and an escaped line break at the end of each line:

```js
var poem =
  "Roses are red,\n\
Violets are blue.\n\
Sugar is sweet,\n\
and so is foo.";
```

ECMAScript 2015 introduces a new type of literal, namely **template literals** (en-US). This allows for many new features including multiline strings!

```js
var poem = `Roses are red,
Violets are blue.
Sugar is sweet,
and so is foo.`;
```

More information
----------------

This chapter focuses on basic syntax for declarations and types. To learn more about JavaScript's language constructs, see also the following chapters in this guide:

* Control flow and error handling
* Loops and iteration
* Functions
* Expressions and operators

In the next chapter, we will have a look at control flow constructs and error handling.

* « 前頁
* 次頁 »


函式 - JavaScript

函式
==

* « 前頁
* 次頁 »

函式是構成 javascript 的基本要素之一。一個函式本身就是一段 JavaScript 程序—包含用於執行某一個任務或計算的語法。要呼叫某一個函式之前，你必需先在這個函式欲執行的 scope 中定義它。

定義函式
----

一個函式的定義由一系列的函式關鍵詞組成, 依次為：

* 函式的名稱。
* 包圍在括號()中，並由逗號區隔的一個函式參數列表。
* 包圍在大括號{}中，用於定義函式功能的一些 JavaScript 語句。

例如，以下的程式碼定義了一個名為 square 的簡單函式:

```js
function square(number) {
  return number \* number;
}
```

函式 square 有一個參數，叫作 number。這個函式只有一行程式碼，它會回傳 number 自乘的結果。函式的 `return` (en-US) 語法描述函式的返回值。

```js
return number \* number;
```

原始參數（例如一個數字）被作為值傳遞給函式，如果呼叫的函式改變了這個參數的值，不會影響到函式外部的原始變數。

如果傳遞一個物件（例如 `Array` (en-US) 或自定義的其它物件）作為參數，而函式改變了這個物件的屬性，這樣的改變對函式外部是有作用的(因為是傳遞物件的位址)，如下面的例子所示：

```js
function myFunc(theObject) {
  theObject.make = "Toyota";
}

var mycar = { make: "Honda", model: "Accord", year: 1998 },
  x,
  y;

x = mycar.make; // x 的值為 "Honda"

myFunc(mycar);
y = mycar.make; // y 的值為 "Toyota"
// (屬性 make 被 function 改變)
```

請注意，重新給參數指定一個對象(物件)，並不會對函式的外部有任何影響，因為這樣只是改變了參數的值，而不是改變了對象的一個屬性值：

```js
function myFunc(theObject) {
  theObject = { make: "Ford", model: "Focus", year: 2006 };
}

var mycar = { make: "Honda", model: "Accord", year: 1998 },
  x,
  y;

x = mycar.make; // x 的值為 "Honda"

myFunc(mycar);
y = mycar.make; // y 的值還是 "Honda"
```

儘管上述函式定義都是用的是陳述式，函式也同樣可以由函式表達式來定義。這樣的函式可以是匿名的；它不必有名稱。例如，上面提到的函式 square 也可這樣來定義：

```js
var square = function (number) {
  return number \* number;
};
var x = square(4); //x 的值為 16
```

必要時，函式名稱可與函式表達式同時存在，並且可以用於在函式內部代指其本身(遞迴)：

```js
var factorial = function fac(n) {
  return n < 2 ? 1 : n \* fac(n - 1);
};

console.log(factorial(3));
```

函式表達式在將函式作為一個參數傳遞給其它函式時十分方便。下面的例子展示了一個叫 map 的函式如何被定義，而後呼叫一個匿名函式作為其第一個參數：

```js
function map(f, a) {
  var result = [], // Create a new Array
    i;
  for (i = 0; i != a.length; i++) result[i] = f(a[i]);
  return result;
}
```

下面的程式碼呼叫 map 函式並將一個匿名函式傳入作為第一個參數:

```js
map(
  function (x) {
    return x \* x \* x;
  },
  [0, 1, 2, 5, 10],
);
// 結果會回傳 [0, 1, 8, 125, 1000]
```

除了上述的定義方式以外，我們也可以透過 `Function` constructor (en-US) 來定義，類似 `eval()`。

呼叫函式
----

定義一個函式並不會自動的執行它。定義了函式僅僅是賦予函式以名稱並明確函式被呼叫時該做些什麼。呼叫函式才會以給定的參數真正執行這些動作。例如，一旦你定義了函式 square，你可以如下這樣呼叫它：

```js
square(5);
```

上述程式碼把 5 傳遞給 square 函式。函式執行完會回傳 25。

函式必須在呼叫區塊的可視範圍內，但函數也可以宣告在使用處的下面，如下列範例:

```js
console.log(square(5));
/\* ... \*/
function square(n) {
  return n \* n;
}
```

The scope of a function is the function in which it is declared, or the entire program if it is declared at the top level. Note that this works only when defining the function using the above syntax (i.e. `function funcName(){}`). The code below will not work.

```js
console.log(square(5));
square = function (n) {
  return n \* n;
};
```

The arguments of a function are not limited to strings and numbers. You can pass whole objects to a function, too. The `show_props` function (defined in Working with Objects (en-US)) is an example of a function that takes an object as an argument.

A function can be recursive; that is, it can call itself. For example, here is a function that computes factorials recursively:

```js
function factorial(n) {
  if (n == 0 || n == 1) return 1;
  else return n \* factorial(n - 1);
}
```

You could then compute the factorials of one through five as follows:

```js
var a, b, c, d, e;
a = factorial(1); // a gets the value 1
b = factorial(2); // b gets the value 2
c = factorial(3); // c gets the value 6
d = factorial(4); // d gets the value 24
e = factorial(5); // e gets the value 120
```

There are other ways to call functions. There are often cases where a function needs to be called dynamically, or the number of arguments to a function vary, or in which the context of the function call needs to be set to a specific object determined at runtime. It turns out that functions are, themselves, objects, and these objects in turn have methods (see the `Function` object (en-US)). One of these, the `apply()` (en-US) method, can be used to achieve this goal.

Function scope
--------------

Variables defined inside a function cannot be accessed from anywhere outside the function, because the variable is defined only in the scope of the function. However, a function can access all variables and functions defined inside the scope in which it is defined. In other words, a function defined in the global scope can access all variables defined in the global scope. A function defined inside another function can also access all variables defined in it's parent function and any other variable to which the parent function has access.

```js
// The following variables are defined in the global scope
var num1 = 20,
  num2 = 3,
  name = "Chamahk";

// This function is defined in the global scope
function multiply() {
  return num1 \* num2;
}

multiply(); // Returns 60

// A nested function example
function getScore() {
  var num1 = 2,
    num2 = 3;

  function add() {
    return name + " scored " + (num1 + num2);
  }

  return add();
}

getScore(); // Returns "Chamahk scored 5"
```

閉包
--

閉包是 JavaScript 最強大的特性之一。JavaScript 允許巢狀函式（nesting of functions）並給予內部函式完全訪問（full access）所有變數、與外部函式定義的函式（還有所有外部函式內的變數與函式）不過，外部函式並不能訪問內部函式的變數與函式。這保障了內部函式的變數安全。另外，由於內部函式能訪問外部函式定義的變數與函式，將存活得比外部函式還久。A closure is created when the inner function is somehow made available to any scope outside the outer function.

```js
var pet = function (name) {
    // The outer function defines a variable called "name"
    var getName = function () {
      return name; // The inner function has access to the "name" variable of the outer function
    };

    return getName; // Return the inner function, thereby exposing it to outer scopes
  },
  myPet = pet("Vivie");

myPet(); // Returns "Vivie"
```

It can be much more complex than the code above. An object containing methods for manipulating the inner variables of the outer function can be returned.

```js
var createPet = function (name) {
  var sex;

  return {
    setName: function (newName) {
      name = newName;
    },

    getName: function () {
      return name;
    },

    getSex: function () {
      return sex;
    },

    setSex: function (newSex) {
      if (
        typeof newSex == "string" &&
        (newSex.toLowerCase() == "male" || newSex.toLowerCase() == "female")
      ) {
        sex = newSex;
      }
    },
  };
};

var pet = createPet("Vivie");
pet.getName(); // Vivie

pet.setName("Oliver");
pet.setSex("male");
pet.getSex(); // male
pet.getName(); // Oliver
```

In the codes above, the `name` variable of the outer function is accessible to the inner functions, and there is no other way to access the inner variables except through the inner functions. The inner variables of the inner function act as safe stores for the inner functions. They hold "persistent", yet secure, data for the inner functions to work with. The functions do not even have to be assigned to a variable, or have a name.

```js
var getCode = (function () {
  var secureCode = "0]Eal(eh&2"; // A code we do not want outsiders to be able to modify...

  return function () {
    return secureCode;
  };
})();

getCode(); // Returns the secret code
```

There are, however, a number of pitfalls to watch out for when using closures. If an enclosed function defines a variable with the same name as the name of a variable in the outer scope, there is no way to refer to the variable in the outer scope again.

```js
var createPet = function (name) {
  // Outer function defines a variable called "name"
  return {
    setName: function (name) {
      // Enclosed function also defines a variable called "name"
      name = name; // ??? How do we access the "name" defined by the outer function ???
    },
  };
};
```

The magical `this` variable is very tricky in closures. They have to be used carefully, as what `this` refers to depends completely on where the function was called, rather than where it was defined. An excellent and elaborate article on closures can be found here.

Using the arguments object
--------------------------

The arguments of a function are maintained in an array-like object. Within a function, you can address the arguments passed to it as follows:

```js
arguments[i];
```

where `i` is the ordinal number of the argument, starting at zero. So, the first argument passed to a function would be `arguments[0]`. The total number of arguments is indicated by `arguments.length`.

Using the `arguments` object, you can call a function with more arguments than it is formally declared to accept. This is often useful if you don't know in advance how many arguments will be passed to the function. You can use `arguments.length` to determine the number of arguments actually passed to the function, and then access each argument using the `arguments` object.

For example, consider a function that concatenates several strings. The only formal argument for the function is a string that specifies the characters that separate the items to concatenate. The function is defined as follows:

```js
function myConcat(separator) {
  var result = "", // initialize list
    i;
  // iterate through arguments
  for (i = 1; i < arguments.length; i++) {
    result += arguments[i] + separator;
  }
  return result;
}
```

You can pass any number of arguments to this function, and it concatenates each argument into a string "list":

```js
// returns "red, orange, blue, "
myConcat(", ", "red", "orange", "blue");

// returns "elephant; giraffe; lion; cheetah; "
myConcat("; ", "elephant", "giraffe", "lion", "cheetah");

// returns "sage. basil. oregano. pepper. parsley. "
myConcat(". ", "sage", "basil", "oregano", "pepper", "parsley");
```

Please note that the `arguments` variable is "array-like", but not an array. It is array-like in that is has a numbered index and a `length` property. However, it does not possess all of the array-manipulation methods.

See the `Function` object (en-US) in the JavaScript Reference for more information.

Predefined functions
--------------------

JavaScript has several top-level predefined functions:

* eval
* isFinite
* isNaN
* parseInt and parseFloat
* Number and String
* encodeURI, decodeURI, encodeURIComponent, and decodeURIComponent (all available with Javascript 1.5 and later).

The following sections introduce these functions. See the JavaScript Reference (en-US) for detailed information on all of these functions.

### eval Function

The `eval` function evaluates a string of JavaScript code without reference to a particular object. The syntax of `eval` is:

```js
eval(expr);
```

where `expr` is a string to be evaluated.

If the string represents an expression, `eval` evaluates the expression. If the argument represents one or more JavaScript statements, eval performs the statements. The scope of `eval` code is identical to the scope of the calling code. Do not call `eval` to evaluate an arithmetic expression; JavaScript evaluates arithmetic expressions automatically.

### isFinite function

The `isFinite` function evaluates an argument to determine whether it is a finite number. The syntax of `isFinite` is:

```js
isFinite(number);
```

where `number` is the number to evaluate.

If the argument is `NaN`, positive infinity or negative infinity, this method returns `false`, otherwise it returns `true`.

The following code checks client input to determine whether it is a finite number.

```js
if (isFinite(ClientInput)) {
  /\* take specific steps \*/
}
```

### isNaN function

The `isNaN` function evaluates an argument to determine if it is "NaN" (not a number). The syntax of `isNaN` is:

```js
isNaN(testValue);
```

where `testValue` is the value you want to evaluate.

The `parseFloat` and `parseInt` functions return "NaN" when they evaluate a value that is not a number. `isNaN` returns true if passed "NaN," and false otherwise.

The following code evaluates `floatValue` to determine if it is a number and then calls a procedure accordingly:

```js
var floatValue = parseFloat(toFloat);

if (isNaN(floatValue)) {
  notFloat();
} else {
  isFloat();
}
```

### parseInt and parseFloat functions

The two "parse" functions, `parseInt` and `parseFloat`, return a numeric value when given a string as an argument.

The syntax of `parseFloat` is:

```js
parseFloat(str);
```

where `parseFloat` parses its argument, the string `str`, and attempts to return a floating-point number. If it encounters a character other than a sign (+ or -), a numeral (0-9), a decimal point, or an exponent, then it returns the value up to that point and ignores that character and all succeeding characters. If the first character cannot be converted to a number, it returns "NaN" (not a number).

The syntax of `parseInt` is:

```js
parseInt(str [, radix]);
```

`parseInt` parses its first argument, the string `str`, and attempts to return an integer of the specified `radix` (base), indicated by the second, optional argument, `radix`. For example, a radix of ten indicates to convert to a decimal number, eight octal, sixteen hexadecimal, and so on. For radixes above ten, the letters of the alphabet indicate numerals greater than nine. For example, for hexadecimal numbers (base 16), A through F are used.

If `parseInt` encounters a character that is not a numeral in the specified radix, it ignores it and all succeeding characters and returns the integer value parsed up to that point. If the first character cannot be converted to a number in the specified radix, it returns "NaN." The `parseInt` function truncates the string to integer values.

### Number and String functions

The `Number` and `String` functions let you convert an object to a number or a string. The syntax of these functions is:

```js
var objRef;
objRef = Number(objRef);
objRef = String(objRef);
```

`objRef 是物件的參照`。 Number uses the valueOf() method of the object; String uses the toString() method of the object.

下列範例將 `日期` (en-US) 物件轉換為可讀字串。

```js
var D = new Date(430054663215),
  x;
x = String(D); // x 等於 "星期二 八月 18 04:37:43 GMT-0700 1983"
```

下列範例將 `字串` (en-US) 物件轉換為 `數字` (en-US) 物件。

```js
var str = "12",
  num;
num = Number(字串);
```

使用 DOM 方法 `write()` 與 JavaScript `typeof` 運算子.

```js
var str = "12",
  num;
document.write(typeof str);
document.write("<br/>");
num = Number(str);
document.write(typeof num);
```

### escape 與 unescape 函式(JavaScript 1.5 後去除)

`escape` 與 `unescape` 對於非 ASCII 字元無法處理。 在 JavaScript 1.5 之後改用 `encodeURI` (en-US), `decodeURI` (en-US), `encodeURIComponent` (en-US), 與 `decodeURIComponent` (en-US).

`escape` 與 `unescape` 用於編碼與解碼字串。 `escape` 函式回傳十六進位編碼。 `unescape` 函式會將十六進位的編碼轉換回 ASCII 字串。

這些函式的語法是:

```js
escape(字串);
unescape(字串);
```

這些函式常被用於伺服器後端中處理姓名等資料。