JavaScript語法

JavaScript程式語言語法是編寫該語言程序的一套規則。

JavaScript標準庫缺少官方標準串流函數（除了`document.write`）。由於JavaScript主要用於現代網頁瀏覽器上執行客戶端腳本，幾乎所有瀏覽器都支援`alert`函數。下列範例中使用標準文本輸出的console對象的log函數。

## 起源

布蘭登·艾克在JavaScript 1.1 規範的第一段中總結如下：JavaScript從Java借來了大部分語法，但也從Awk和Perl繼承，在其對象原型系統中還受到 Self 的一些間接影響。

## 基礎

### 大小寫敏感

JavaScript是大小寫敏感的。經常以一個大寫字母開始構造函數的名稱，一個小寫字母開始函數或變量的名字。

例子：

```javascript
var a = 5;
console.log(a); // 5
console.log(A); // throws a ReferenceError: A is not defined
```

### 空白和分號

不像在C語言中，JavaScript原始碼中的空白可以直接影響形式語義學。JavaScript里的語句用分號結束。因為自動分號插入（ASI），也就是說，一些符合語法規則的的語句在分析到換行符時會被認為是完整的，如同在換行符前面插入了一個分號。一些當局建議顯式地寫結束語句的分號，因為它也許會減少自動分號插入未曾預料的效果。

有兩個問題：有五個符記既能開始一條語句，也能擴展一條完整的語句；以及五個受限的語句（production），其中換行在一些位置不被允許，潛在可能會產生不正確的解析。

五個有問題的符記是左圓括號「`(`」，左方括號「`[`」，斜線「`/`」，加號「`+`」，減號「`-`」。當然，左圓括號在立即調用函數表達式模式中很常見，左方括號有時出現，其他幾個就很少了。規範中給出的示例是：

```javascript
a = b + c
(d + e).foo()

// Treated as:
//  a = b + c(d + e).foo();
```

有建議前面的語句以分號結束。

有些人建議在以「`(`」或「`[`」開始的行，使用前導分號，讓這行不會意外地與上一行連接。這被稱為防禦性分號，特別推薦，因為否則在重新排列代碼時可能變得模稜兩可。例如：

```javascript
a = b + c
(d + e).foo()

// Treated as:
//  a = b + c(d + e).foo();
```

最初的分號有時也在JavaScript庫中使用，在它們被追加到另一個省略尾部分號的庫的情況下，因為這可能會導致初始語句的模糊不清。

五個受限的語句是`return`，`throw`，`break`，`continue`，和後導遞增或遞減。在所有情況里，插入分號不會解決問題，但使得解析的語法更加清晰，錯誤更加容易檢測。`return` 和 `throw` 取一個可選的值，`break` 和 `continue` 取一個可選的標籤。所有情況下，都建議使值或標籤和語句保持在一行上。最多的情況是返回語句，一個人可能想返回一個大的對象文字，它可能意外地被放在了新行上。對於後導遞增或遞減，有可能與前綴遞增或遞減模稜兩可，所以再一次推薦把這些放在同一行。

```javascript
return
a + b;

// Returns undefined. Treated as:
//   return;
//   a + b;
// Should be written as:
//   return a + b;
```

### 注釋

注釋語法和C++,Swift,和許多其他程式語言相同。

```javascript
// a short, one-line comment

/* this is a long, multi-line comment
  about my script. May it one day
  be great. */

/* Comments /* may not be nested */ Syntax error */
```

## 變量

標準JavaScript中的變量沒有附加類型，因此任何值（每個值都有一個類型）可以存儲在任何變量中。從ES6（該語言的第6版）開始，變量可以用`var`聲明函數範圍變量，而`let`或`const`用於塊級變量。在ES6之前，變量只能用`var`語句聲明。分配給用`const`聲明的變量的值不能更改，但其屬性可以。變量的標識符必須以字母、下劃線 (`_`) 或美元符號 (`$`) 開頭，而後續字符也可以是數字 (`0-9`)。JavaScript區分大小寫，因此大寫字符「A」到「Z」與小寫字符「a」到「z」不同。

從JavaScript1.5 開始，可以在標識符中使用ISO 8859-1或Unicode字符（或 `\uXXXX`Unicode轉義序列）。在某些JavaScript實現中，可以在標識符中使用at符號 (@)，但這與規範相反，並且在較新的實現中不受支持。

### 作用域和提升

用`var`聲明的變量是在函數級別的詞法作用域，而用`let`或`const`聲明的變量具有塊級作用域。由於聲明是在任何代碼執行之前處理的，因此可以在代碼中聲明之前分配和使用變量。這被稱為提升(hoisting)，它相當於在函數或塊的頂部前向聲明的變量。

對於`var`、`let`和`const`語句，只有聲明被提升；賦值未被提升。因此，函數中間的語句等效於函數頂部的var x聲明語句，以及函數中間該點的賦值語句。這意味著值在聲明之前不能被訪問；前向引用(forward reference)是不可能的。使用`var`變量的值在初始化之前是未定義的(`undefined`)。使用`let`或`const`聲明的變量在初始化之前無法訪問，因此之前引用此類變量會導致錯誤。

函數聲明，它聲明一個變量並為其分配一個函數，類似於變量語句，但除了可以提升聲明之外，它們還提升了賦值——就好像整個語句出現在包含的函數的頂部一樣——因此前向引用(forward reference)也是允許的：函數語句在包含它的函數中的位置是不相關的。這不同於在`var`、`let`或`const`語句中將函數表達式賦值給變量。

因而，如：

```javascript
var func = function() { .. } // 仅提升声明
function func() { .. } // 声明和赋值被提升
```

塊作用域可以通過將整個塊包裝在一個函數中然後執行它來產生——這被稱為立即調用函數表達式模式——或者通過使用`let`關鍵字聲明變量。

### 聲明和賦值

在作用域外聲明的變量是全局變量。如果一個變量在更高的作用域中聲明，它可以被子作用域(child scope)訪問。

當JavaScript嘗試解析標識符時，它會在本地作用域內查找。如果沒有找到這個標識符，它會在下一個外部作用域中查找，依此類推，直到抵達全局變量所在的全局作用域。如果仍未找到，JavaScript將引發`ReferenceError`異常。

給標識符賦值時，JavaScript會通過完全相同的過程來獲取此標識符；但如果在全局範圍內找不到它，將在創建的作用域內創建「變量」。因此，如果給未聲明的變量賦值，會被創建為一個全局變量。在全局範圍內（即在任何函數體之外或在`let/const`的情況下為塊）聲明一個變量（使用關鍵字`var`），或者給一個未聲明的標識符賦值，或向全局對象（通常是窗口）添加一個屬性，這將創建一個新的全局變量。

請注意，JavaScript的嚴格模式(strict mode)禁止給未聲明的變量賦值，從而避免了全局命名空間污染。

### 例子

變量聲明和作用域的一些例子：

```javascript
var x1 = 0; // 一个全局变量，因为未在任何函数内
let x2 = 0; // 也是全局变量，因为未在任何块内

function f() {
  var z = 'foxes', r = 'birds'; // 2个局部变量
  m = 'fish'; //全局变量，因为给未声明的变量赋值

  function child() {
    var r = 'monkeys'; //为局部变量，不影响父函数中变量r的值"birds"
    z = 'penguins'; //闭包：子函数也能访问父函数中的变量
  }

  twenty = 20; //该变量在下一行声明，但在该函数内任何位置都可用，这里是在声明之前使用
  var twenty;

  child();
  return x1 + x2; //这里使用的x1和x2是全局变量
}

f();

console.log(z); // 这将抛出ReferenceError异常，因为z不可用了
```

```javascript
for (let i = 0; i < 10; i++) console.log(i);
console.log(i); // 抛出 ReferenceError: i is not defined
```

```javascript
for (const i = 0; i < 10; i++) console.log(i); // 抛出TypeError: Assignment to constant variable

const pi; // 抛出SyntaxError: Missing initializer in const declaration
```

## 基本數據類型

JavaScript語言提供6種基本數據類型:

- Undefined
- Number
- BigInt
- String
- Boolean
- Symbol

一些基本數據類型還提供一組命名值來表示類型邊界的範圍。這些命名值在下面的相應子節中描述。

### Undefined

未定義值被賦值給所有未初始化變量，在查看不存在的對象屬性時也返回這個值。在布爾上下文中，未定義值被認為是假值。

注意： undefined 被認為是真正的基本數據類型。除非顯式轉換，否則與在邏輯上下文中評估為false的其他類型相比，未定義的值可能會出現不可預料的行為。

```javascript
var test;                         // 变量被声明但未定义，其值被赋为undefined值

var testObj = {};
console.log(test);                // test的值存在，显示为undefined
console.log(testObj.myProp);      // testObj存在，但属性不存在，显示为undefined
console.log(undefined == null);   // 未强制类型检查，显示为true
console.log(undefined === null);  // 强制类型检查，显示为false
```

注意：沒有內建的語言字面量用於undefined。因此並不是一個萬無一失的放式檢查一個變量是否為undefined，因為在ECMAScript 5之前的版本，如果寫代碼是合法的。更魯棒的比較方法是.

下屬函數不會如期望那樣工作：

```javascript
function isUndefined(x) { var u; return x === u; }             // 如这个...
function isUndefined(x) { return x === void 0; }               // ... 或第二个
function isUndefined(x) { return (typeof x) === "undefined"; } // ... 或第三个
```

調用`isUndefined(my_var)`會拋出ReferenceError，如果my_var是未知標識符，但不會拋出異常。

### Number

Number數據類型表示IEEE-754浮點雙精度。不能精確表示一些實數或分數。如：

```javascript
console.log(0.2 + 0.1 === 0.3); // displays false
console.log(0.94 - 0.01);       // displays 0.9299999999999999
```

因此，無論何時格式化輸出，都應使用諸如toFixed方法之類的例程對數字進行捨入。

數字可以用這些符號中的任何一種來指定：

```javascript
345;    // 一个整数
34.5;   // 一个浮点数
3.45e2; // 另一个浮点数
0b1011; // 二进制整数
0o377;   // 八进制整数
0xFF;   // 十六进制整数，A-F可大写可小写
```

ES2021引入了數字分隔符_ (the underscore)：

```javascript
1_000_000_000;    // 用于大数
1_000_000.5;      // 支持十进制
1_000e1_000;      // 支持科学计数法

//支持二进制、八进制、十六进制
0b0000_0000_0101_1011;
0o0001_3520_0237_1327;
0xFFFF_FFFF_FFFF_FFFE;

// 但是您能在数的非数字部分旁边，或开头或结尾处使用分隔符
_12; // 变量未定义（下划线使其成为变量标识符）
12_; // 语法错误（不能在数字的末尾）
12_.0; // 语法错误（在小数点旁边放置分隔符没有意义）
12._0; // 语法错误
12e_6; // 语法错误（“e”旁边，非数字。在开头放置分隔符没有意义）
1000____0000; // 语法错误（“_”旁边，非数字。一次只允许 1 个分隔符
```

Number類型的範圍+∞, −∞和NaN(Not a Number)可以通過兩個程序表達式獲得：

```javascript
Infinity; // 正无穷大（用-Infinity 获得负无穷大）
NaN;      // Not-A-Number值，字符串到数字转换时也作为失败返回值
```

Infinity 和 NaN 是數字：

```javascript
typeof Infinity;   // 返回"number"
typeof NaN;        // 返回"number"
```

這三個特殊值對應並按照IEEE-754描述的方式運行。

Number構造函數（用作函數），或一元+或-可用於執行顯式數值轉換：

```javascript
var myString = "123.456";
var myNumber1 = Number(myString);
var myNumber2 = +myString;
```

當用作構造函數時，會創建一個數值包裝對象（儘管它用處不大）：

```javascript
myNumericWrapper = new Number(123.456);
```

但是，NaN 不等於自身：

```javascript
const nan = NaN;
console.log(NaN == NaN);        // false
console.log(NaN === NaN);       // false
console.log(NaN !== NaN);       // true
console.log(nan !== nan);       // true

//可用isNaN方法检查NaN
console.log(isNaN("converted to NaN"));     // true
console.log(isNaN(NaN));                    // true
console.log(Number.isNaN("not converted")); // false
console.log(Number.isNaN(NaN));             // true
```

### BigInt

BigInts 可用於任意大的整數。特別是大於253 - 1的整數，這是JavaScript可以用 Number 原語可靠表示的最大數字，並由Number.MAX_SAFE_INTEGER常量表示。

```javascript
let t = 100n;
let t1 = BigInt('100');
let t2 = BigInt('0x64');
let t3 = BigInt(100);
//这几种方法，都可以定义BigInt
```

Math的方法不支持BigInt。

JSON.stringify不支持BigInt，需要`BigInt.prototype.toJSON = function() { return this.toString(); }`後就可以支持。

toString方法支持BigInt，radix有效值是2,36，默認是10。

BigInt除法，結果會被截斷為整數，不是浮點數。如：100n/7n;結果為14n，不是浮點數。

### String

JavaScript中的字串是一個字符序列。在JavaScript中，可以通過將一系列字符放在雙引號(") 或單引號(')之間直接創建字串（作為字面量）。此種字串必須寫在單行上，但可包含轉義的換行符（如\n). JavaScript標準允許反引號字符（`，即重音符或反撇號）引用多行字串字面量，但這僅在2016年開始的某些瀏覽器上支持：Firefox和Chrome，但Internet Explorer 11不支持。

```javascript
var greeting = "Hello, World!";
var anotherGreeting = 'Greetings, people of Earth.';
```

可以使用charAt方法（由String.prototype提供）訪問字串中的單個字符。這是訪問字串中的單個字符時的首選方式，因為它也適用於非現代瀏覽器：

```javascript
var h = greeting.charAt(0);
```

在現代瀏覽器中，可以通過與陣列相同的表示法訪問字串中的單個字符：

```javascript
var h = greeting[0];
```

但是，JavaScript字串是不可變的：

```javascript
greeting[0] = "H"; // Fails.
```

如果字串具有相同的內容，則將相等運算符 ("==") 應用於兩個字串將返回 true，這意味著：具有相同的長度並包含相同的字符序列（大小寫對字母很重要）。因此：

```javascript
var x = "World";
var compare1 = ("Hello, " +x == "Hello, World"); // Here compare1 contains true.
var compare2 = ("Hello, " +x == "hello, World"); // Here compare2 contains false since the first characters of the second operands are not of the same case.
```

除非轉義，否則不能嵌套相同類型的引號：

```javascript
var x = '"Hello, World!" he said.'; // Just fine.
var x = ""Hello, World!" he said."; //  Not good.
var x = "\"Hello, World!\" he said."; // Works by escaping " with \"
```

String構造函數創建一個字串對象（一個包裝字串的對象）：

```javascript
var greeting = new String("Hello, World!");
```

這些對象有一個valueOf方法，返回包裝在其中的原始字串：

```javascript
var s = new String("Hello !");
typeof s; // Is 'object'.
typeof s.valueOf(); // Is 'string'.
```

兩個String對象之間的相等與字串原語不同：

```javascript
var s1 = new String("Hello !");
var s2 = new String("Hello !");
s1 == s2; // Is false, because they are two distinct objects.
s1.valueOf() == s2.valueOf(); // Is true.
```

### Boolean

JavaScript提供了一個帶有真假字面量的布爾數據類型。typeof運算符為這些基本類型返回字串「"boolean"」。在邏輯上下文中使用時，由於自動類型轉換，0、-0、null、NaN、undefined和空字符串求值為false。所有其他值（前一個列表的補集）求值為true，包括字串"0"、"false"和任何對象。

### 類型轉換

使用類型檢查的比較運算符(`===`和`!==`)可以避免相等比較運算符(`==`和`!=`)的自動類型強制(type coercion)。

當需要類型轉換時，JavaScript會按如下方式轉換Boolean、Number、String或Object操作數：

Number和String:字串被轉換為數字值。JavaScript嘗試將字串數字字面量轉換為數字類型的值。首先，從字串數字字面量推導出數學值。接下來將此值四捨五入到最接近的數字類型值。

Boolean: 如果其中一個操作數是布爾值，則該布爾操作數如果為true則轉換為 1，如果為false則轉換為 0。

Object:如果將對象與數字或字串進行比較，JavaScript會嘗試返回該對象的默認值。使用對象的.valueOf或.toString方法將對象轉換為基本String或Number值。如果失敗，則會生成運行時錯誤。

道格拉斯·克羅克福特提倡用「truthy」和「falsy」來描述各種類型的值在邏輯上下文中的行為方式，特別是在邊緣情況下。在JavaScript的早期版本中，二元邏輯運算符返回一個布爾值，但現在它們返回一個操作數。在合取(`a && b`)時，如果左操作數為false，則返回左操作數；或在析取(`a

b`)时，如果左操作数为true，则返回左操作数；否则返回右操作数。对于混合了布尔操作数和number兼容操作数（包括可以计算为number的字符串，或可以计算为此类字符串的对象），比较运算符的自动类型强制可能会有所不同，因为布尔操作数将作为一个数值被比较。这可能出乎意料。表达式可以通过双重逻辑否定运算符：、使用Boolean函数或使用条件运算符：(`c ? t : f`)顯式轉換為布爾基本類型。

```javascript
// Automatic type coercion
console.log(true  ==   2 ); // false... true  → 1 !== 2 ←  2
console.log(false ==   2 ); // false... false → 0 !== 2 ←  2
console.log(true  ==   1 ); // true.... true  → 1 === 1 ←  1
console.log(false ==   0 ); // true.... false → 0 === 0 ←  0
console.log(true  ==  "2"); // false... true  → 1 !== 2 ← "2"
console.log(false ==  "2"); // false... false → 0 !== 2 ← "2"
console.log(true  ==  "1"); // true.... true  → 1 === 1 ← "1"
console.log(false ==  "0"); // true.... false → 0 === 0 ← "0"
console.log(false ==  "" ); // true.... false → 0 === 0 ← ""
console.log(false ==  NaN); // false... false → 0 !== NaN

console.log(NaN == NaN); // false...... NaN is not equivalent to anything, including NaN.

// Type checked comparison (no conversion of types and values)
console.log(true === 1); // false...... data types do not match

// Explicit type coercion
console.log(true === 

2);   // true.... data types and values match
console.log(true === 

0);   // false... data types match, but values differ
console.log( 1  ? true : false); // true.... only ±0 and NaN are "falsy" numbers
console.log("0" ? true : false); // true.... only the empty string is "falsy"
console.log(Boolean({}));    // true.... all objects are "truthy"
```

new運算符可用於為布爾基本類型創建對象包裝器。但是，typeof運算符不返回對象包裝器的boolean，它返回object。因為所有對象都求值為true，所以必須使用諸如.valueOf或.toString之類的方法來檢索包裝的值。對於Boolean類型的顯式強制，Mozilla建議優先使用Boolean函數（沒有）而不是 Boolean 對象。

```javascript
var b = new Boolean(false);   // Object  false {}
var t = Boolean(b);           // Boolean true
var f = Boolean(b.valueOf()); // Boolean false
var n = new Boolean(b);       // Not recommended
n = new Boolean(b.valueOf()); // Preferred

if (0 || -0 || "" || null || undefined || b.valueOf() || !new Boolean() || !t) {
  console.log("Never this");
} else if ([] && {} && b && typeof b === "object" && b.toString() === "false") {
  console.log("Always this");
}
```

### Symbol

ECMAScript6引入的新類型。一個Symbol是獨一無二且不可變的標識符。

例如：

```javascript
var x = Symbol(1);
var y = Symbol(1);
console.log(x === y); // => false

var symbolObject = {};
var normalObject = {};

// since x and y are unique,
// they can be used as unique keys in an object
symbolObject[x] = 1;
symbolObject[y] = 2;

console.log(symbolObject[x]); // => 1
console.log(symbolObject[y]); // => 2

// as compared to normal numeric keys
normalObject[1] = 1;
normalObject[1] = 2; // overrides the value of 1

console.log(normalObject[1]); // => 2

// changing the value of x does not change the key stored in the object
x = Symbol(3);
console.log(symbolObject[x]); // => undefined

// changing x back just creates another unique Symbol
x = Symbol(1);
console.log(symbolObject[x]); // => undefined
```

存在well known symbols。其中一個是`Symbol.iterator`; 如果程序實現了`Symbol.iterator`，則它是可迭代的：

```javascript
let x = [1, 2, 3, 4]; // x is an Array
x[Symbol.iterator] === Array.prototype[Symbol.iterator]; // and Arrays are iterable

const xIterator = x[Symbol.iterator](); // The [Symbol.iterator] function should provide an iterator for x
xIterator.next(); // { value: 1, done: false }
xIterator.next(); // { value: 2, done: false }
xIterator.next(); // { value: 3, done: false }
xIterator.next(); // { value: 4, done: false }
xIterator.next(); // { value: undefined, done: true }
xIterator.next(); // { value: undefined, done: true }

// for..of loops automatically iterate values
for (const value of x) {
   console.log(value); // 1 2 3 4
}

// Sets are also iterable:
[Symbol.iterator] in Set.prototype; // true

for (const value of new Set(['apple', 'orange'])) {
   console.log(value); // "apple" "orange"
}
```

## 原生對象

JavaScript語言提供了一些原生對象(native object)。JavaScript原生對象被認為是JavaScript規範的一部分。儘管有JavaScript環境，但這組對象應該始終可用。

### Array

陣列(array)是從`Array`構造函數原型化的JavaScript對象，專門設計用於存儲由整數鍵索引的數據值。陣列與基本的Object類型不同，它是具有方法和屬性的原型，以幫助程式設計師完成日常任務（例如，`join`, `slice`和`push`）。

與C語言家族的程式語言一樣，陣列使用從零開始的索引方案：通過`push`方法插入空陣列的值占據陣列的第0個索引。

```javascript
var myArray = [];            // Point the variable myArray to a newly ...
                             // ... created, empty Array
myArray.push("hello World"); // Fill the next empty index, in this case 0
console.log(myArray[0]);           // Equivalent to console.log("hello World");
```

陣列有一個`length`屬性，保證總是大於陣列中使用的最大整數索引。如果創建具有更大索引的屬性，它會自動更新。將較小的數字寫入`length`屬性將刪除較大的索引。

可以使用普通對象屬性訪問表示法訪問`Array`的元素：

```javascript
myArray[1];  // the 2nd item in myArray
myArray["1"];
```

以上兩個是等價的。不能使用「點」表示法或帶有替代數字表示的字串：

```javascript
myArray.1;     // syntax error
myArray["01"]; // not the same as myArray[1]
```

聲明一個陣列可用`Array`字面量或者`Array`構造函數:

```javascript
let myArray;

// Array literals
myArray = [1, 2]; // length of 2
myArray = [1, 2,]; // same array - You can also have an extra comma at the end

// It's also possible to not fill in parts of the array
myArray = [0, 1, /* hole */, /* hole */, 4, 5]; // length of 6
myArray = [0, 1, /* hole */, /* hole */, 4, 5,]; // same array
myArray = [0, 1, /* hole */, /* hole */, 4, 5, /* hole */,]; // length of 7

// With the constructor
myArray = new Array(0, 1, 2, 3, 4, 5); // length of 6
myArray = new Array(365);              // an empty array with length 365
```

陣列實現為「稀疏陣列」，只有定義的元素占用了記憶體。設置和只為這兩個元素分配了記憶體空間，這與任何其他對象一樣。陣列的`length`仍報告為58。陣列的最大長度為4,294,967,295，對應於 32 位二進位無符號數(11111111111111111111111111111111)2。

可以使用對象聲明字面量來創建其行為類似於其他語言中的關聯數組對象：

```javascript
dog = {color: "brown", size: "large"};
dog["color"]; // results in "brown"
dog.color;    // also results in "brown"
```

可以使用對象和陣列聲明字面量來快速創建關聯數組、多維陣列或兩者兼有的陣列。從技術上講，JavaScript不支持多維陣列，但可以使用陣列陣列來模仿它們：

```javascript
cats = [{color: "brown", size: "large"},
    {color: "black", size: "small"}];
cats[0]["size"];      // results in "large"

dogs = {rover: {color: "brown", size: "large"},
    spot: {color: "black", size: "small"}};
dogs["spot"]["size"]; // results in "small"
dogs.rover.color;     // results in "brown"
```

### Date

`Date`對象存儲有符號的毫秒計數，零表示1970-01-01 00:00:00 UT，值域為±108 天。有幾種方法可以為`Date`構造函數提供參數。注意，月份是從零開始的。

```javascript
new Date();                       // create a new Date instance representing the current time/date.
new Date(2010, 2, 1);             // create a new Date instance representing 2010-Mar-01 00:00:00
new Date(2010, 2, 1, 14, 25, 30); // create a new Date instance representing 2010-Mar-01 14:25:30
new Date("2010-3-1 14:25:30");    // create a new Date instance from a String.
```

提供了抽取欄位的方法，以及有用的`toString`:

```javascript
var d = new Date(2010, 2, 1, 14, 25, 30); // 2010-Mar-01 14:25:30;

// Displays '2010-3-1 14:25:30':
console.log(d.getFullYear() + '-' + (d.getMonth() + 1) + '-' + d.getDate() + ' '
    + d.getHours() + ':' + d.getMinutes() + ':' + d.getSeconds());

// Built-in toString returns something like 'Mon Mar 01 2010 14:25:30 GMT-0500 (EST)':
console.log(d);
```

### Error

用`Error`類可以創建定製的錯誤消息:

```javascript
throw new Error("Something went wrong.");
```

這可被try...catch...finally塊捕獲。將在異常處理節中詳述。

### Math

The Math object contains various math-related constants (for example, ) and functions (for example, cosine). (Note that the object has no constructor, unlike or . All its methods are "static", that is "class" methods.) All the trigonometric functions use angles expressed in radians, not degrees or grads.

Math對象包含各種與數學相關的常數（例如，Π）和函數（例如，餘弦）。請注意，與Array或Date不同，Math對象沒有構造函數。它的所有方法都是「靜態的」，即「類」方法。所有三角函數都使用以弧度表示角度，而不是度數或百分度。

屬性｜返回值四捨五入到5位數字｜說明

Math.E｜2.7183｜e: 自然對數基

Math.LN2｜0.69315｜2的自然對數

Math.LN10｜2.3026｜10的自然對數

Math.LOG2E｜1.4427｜e的以2為基的對數

Math.LOG10E｜0.43429｜e的以10為基的對數

Math.PI｜3.14159｜Π: 圓周率

Math.SQRT1_2｜0.70711｜½的平方根

Math.SQRT2｜1.4142｜2的平方根

例子｜返回值四捨五入到5位數字｜說明

Math.abs(-2.3)｜2.3｜絕對值

Math.acos(Math.SQRT1_2)｜0.78540rad = 45°｜反餘弦

Math.asin(Math.SQRT1_2)｜0.78540rad = 45°｜反正弦

Math.atan(1)｜0.78540rad = 45°｜半圓反正切 (-\pi/2至+\pi/2)

Math.atan2(-3.7, -3.7)｜-2.3562rad = -135deg｜全圓反正切 (-\pi至+\pi)

Math.ceil(1.1)｜2｜Ceiling: 近似到最小整數≥參數

Math.cos(Math.PI/4)｜0.70711｜餘弦函數

Math.exp(1)｜2.7183｜指數函數: e的指數值

Math.floor(1.9)｜1｜Floor:近似到最大整數≤參數

Math.log(Math.E)｜1｜基於e的自然對數

Math.max(1, -2)｜1｜最大值: (x > y) ? x : y

Math.min(1, -2)｜-2｜最小值: (x < y) ? x : y

Math.pow(-3, 2)｜9｜指數運算: Math.pow(x, y)返回xy

Math.random｜e.g. 0.17068｜偽隨機數，值域0,1)

Math.round(1.5)｜2｜四捨五入，1.5近似到2

Math.sin(Math.PI/4)｜0.70711｜正弦

Math.sqrt(49)｜7｜平方根

Math.tan(Math.PI/4)｜1｜正切

### 正則表達式

```javascript
/expression/.test(string);     // returns Boolean
"string".search(/expression/); // returns position Number
"string".replace(/expression/, replacement);

// Here are some examples
if (/Tom/.test("My name is Tom")) console.log("Hello Tom!");
console.log("My name is Tom".search(/Tom/));          // == 11 (letters before Tom)
console.log("My name is Tom".replace(/Tom/, "John")); // == "My name is John"
```

#### 字符類

```javascript
// \d  - digit
// \D  - non digit
// \s  - space
// \S  - non space
// \w  - word char
// \W  - non word
// [ ] - one of
// [^] - one not of
//  -  - range

if (/\d/.test('0'))                   console.log('Digit');
if (/[0-9]/.test('6'))                console.log('Digit');
if (/[13579]/.test('1'))              console.log('Odd number');
if (/\S\S\s\S\S\S\S/.test('My name')) console.log('Format OK');
if (/\w\w\w/.test('Tom'))             console.log('Hello Tom');
if (/[a-zA-Z]/.test('B'))             console.log('Letter');
```

#### 字符匹配

```javascript
// A...Z a...z 0...9  - alphanumeric
// \u0000...\uFFFF    - Unicode hexadecimal
// \x00...\xFF        - ASCII hexadecimal
// \t                 - tab
// \n                 - new line
// \r                 - CR
// .                  - any character
// |                  - OR

if (/T.m/.test('Tom')) console.log ('Hi Tom, Tam or Tim');
if (/A|B/.test("A"))  console.log ('A or B');
```

#### 重複

```javascript
// ?   - 0 or 1 match
// *   - 0 or more
// +   - 1 or more
// {n}   - exactly n
// {n,}  - n or more
// {0,n} - n or less
// {n,m} - range n to m

if (/ab?c/.test("ac"))       console.log("OK"); // match: "ac", "abc"
if (/ab*c/.test("ac"))       console.log("OK"); // match: "ac", "abc", "abbc", "abbbc" etc.
if (/ab+c/.test("abc"))      console.log("OK"); // match: "abc", "abbc", "abbbc" etc.
if (/ab{3}c/.test("abbbc"))  console.log("OK"); // match: "abbbc"
if (/ab{3,}c/.test("abbbc")) console.log("OK"); // match: "abbbc", "abbbbc", "abbbbbc" etc.
if (/ab{1,3}c/.test("abc"))  console.log("OK"); // match: "abc", "abbc", "abbbc"
```

#### Anchors

```javascript
// ^  - string starts with
// $  - string ends with

if (/^My/.test("My name is Tom"))   console.log ("Hi!");
if (/Tom$/.test("My name is Tom"))  console.log ("Hi Tom!");
```

#### 子表達式

```javascript
// ( )  - groups characters

if (/water(mark)?/.test("watermark")) console.log("Here is water!"); // match: "water", "watermark",
if (/(Tom)|(John)/.test("John"))      console.log("Hi Tom or John!");
```

#### Flag

```javascript
// /g  - global
// /i  - ignore upper/lower case
// /m  - allow matches to span multiple lines

console.log("hi tom!".replace(/Tom/i, "John"));  // == "hi John!"
console.log("ratatam".replace(/ta/, "tu"));      // == "ratutam"
console.log("ratatam".replace(/ta/g, "tu"));     // == "ratutum"
```

#### 高級方法

```javascript
my_array = my_string.split(my_delimiter);
// example
my_array = "dog,cat,cow".split(",");      // my_array==["dog","cat","cow"];

my_array = my_string.match(my_expression);
// example
my_array = "We start at 11:30, 12:15 and 16:45".match(/\d\d:\d\d/g); // my_array==["11:30","12:15","16:45"];
```

#### Capturing group

```javascript
var myRe = /(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2})/;
var results = myRe.exec("The date and time are 2009-09-08 09:37:08.");
if (results) {
  console.log("Matched: " + results[0]); // Entire match
  var my_date = results[1]; // First group == "2009-09-08"
  var my_time = results[2]; // Second group == "09:37:08"
  console.log("It is " + my_time + " on " + my_date);
} else console.log("Did not find a valid date!");
```

### 函數

JavaScript的每個函數是`Function`構造符的一個實例。當然還有async / generator函數構造符：`(async()=>{})`構造符得到`AsyncFunction`；`(async function*(){})`構造符得到`AsyncGeneratorFunction`； `(function*(){})`構造符得到`GeneratorFunction`。

```javascript
// x, y is the argument. 'return x + y' is the function body, which is the last in the argument list.
var add = new Function('x', 'y', 'return x + y');
add(1, 2); // => 3
```

上述加法函數也可定義為函數表達式:

```javascript
var add = function(x, y) {
  return x + y;
};
add(1, 2); // => 3
```

ES6中，增加了箭頭函數語法(arrow function syntax)。與`function() {}`表達式不同，它們還保留全局對象的`this`而不是從調用它的位置/調用時的值來繼承它。

```javascript
var add = (x, y) => {return x + y;};
// values can also be implicitly returned (i.e. no return statement is needed)
var addImplicit = (x, y) => x + y;

add(1, 2); // => 3
addImplicit(1, 2) // => 3
```

對於需要提升的函數，專門的表達式：

```javascript
function add(x, y) {
  return x + y;
}
add(1, 2); // => 3
```

提升允許你在聲明之前使用它：

```javascript
add(1, 2); // => 3, not a ReferenceError
function add(x, y) {
  return x + y;
}
```

函數實例可具有屬性和方法：

```javascript
function subtract(x, y) {
  return x - y;
}

console.log(subtract.length); // => 2, arity of the function (number of arguments)
console.log(subtract.toString());

/*
"function subtract(x, y) {
  return x - y;
}"
*/
```

## 運算符

'+'運算符重載：它用於字串連接和算術加法。當無意中混合字符串和數字時，這可能會導致問題。作為一元運算符，它可以將數字字串轉換為數字。

```javascript
// Concatenate 2 strings
console.log('He' + 'llo'); // displays Hello

// Add two numbers
console.log(2 + 6);  // displays 8

// Adding a number and a string results in concatenation (from left to right)
console.log(2 + '2');    // displays 22
console.log('$' + 3 + 4);  // displays $34, but $7 may have been expected
console.log('$' + (3 + 4)); // displays $7
console.log(3 + 4 + '7'); // displays 77, numbers stay numbers until a string is added

// Convert a string to a number using the unary plus
console.log(+'2' === 2); // displays true
console.log(+'Hello'); // displays NaN
```

類似地， '*'運算符也是重載的：可以把字串轉為數。

```javascript
console.log(2 + '6'*1);  // displays 8
console.log(3*'7'); // 21
console.log('3'*'7'); // 21
console.log('hello'*'world'); // displays NaN
```

### 算術運算符

JavaScript支持下述二元算術運算符：

`+`｜加法

`-`｜加法

`*`｜乘法

`/`｜除法(返回浮點值)

`%`｜求余(返回餘數)

`**`｜指數

JavaScript支持下述酉算術運算符:

`+`｜字串轉數

`-`｜符號取反

`++`｜前增或後增

`--`｜前減或後減

例如：

```javascript
var x = 1;
console.log(++x); // x becomes 2; displays 2
console.log(x++); // displays 2; x becomes 3
console.log(x);  // x is 3; displays 3
console.log(x--); //  displays 3; x becomes 2
console.log(x);  //  displays 2; x is 2
console.log(--x); //  x becomes 1; displays 1
```

模運算符顯示除以模數後的餘數。如果涉及負數，則返回值取決於操作數。

```javascript
var x = 17;
console.log(x%5); // displays 2
console.log(x%6); // displays 5
console.log(-x%5); // displays -2
console.log(-x%-5); // displays -2
console.log(x%-5); // displays 2
```

要始終返回非負數，請重新添加模數並再次應用模運算符:

```javascript
var x = 17;
console.log((-x%5+5)%5); // displays 3
```

### 賦值複合運算符

`=`｜賦值

`+=`｜加法賦值

`-=`｜減法賦值

`*=`｜乘法賦值

`/=`｜除法賦值

`%=`｜求余賦值

`**=`｜指數賦值

基本類型的賦值：

```javascript
var x = 9;
x += 1; 
console.log(x); // displays: 10
x *= 30;
console.log(x); // displays: 300
x /= 6;
console.log(x); // displays: 50
x -= 3;
console.log(x); // displays: 47
x %= 7;
console.log(x); // displays: 5
```

對象類型的賦值：

```javascript
/**
 * To learn JavaScript objects...
 */
var object_1 = {a: 1};		// assign reference of newly created object to object_1
var object_2 = {a: 0};
var object_3 = object_2;	// object_3 references the same object as object_2 does
	 
object_3.a = 2;
message();	        	// displays 1 2 2
	 
object_2 = object_1;		// object_2 now references the same object as object_1
	        	        // object_3 still references what object_2 referenced before
message();		        // displays 1 1 2
	 
object_2.a = 7;  	        // modifies object_1
message();		        // displays 7 7 2

object_3.a = 5;                 // object_3 doesn't change object_2
message();	                // displays 7 7 5

object_3 = object_2;	
object_3.a=4;                  // object_3 changes object_1 and object_2
message();                     // displays 4 4 4

/**
 * Prints the console.log message
 */
function message() {
	console.log(object_1.a + " " + object_2.a + " " + object_3.a);
}
```

#### 解構賦值

```
at its leaves that are to receive the substructures of the assigned value.
```

在Mozilla的JavaScript中，從1.7版開始，解構賦值(destructuring assignment)允許一次將部分數據結構賦值給多個變量。賦值的左側是一個類似於任意嵌套對象/陣列字面量的模式，在其葉子處包含左值將接收賦值的子結構。

```javascript
var a, b, c, d, e;
[a, b, c] = [3, 4, 5];
console.log(a + ',' + b + ',' + c); // displays: 3,4,5

e = {foo: 5, bar: 6, baz: ['Baz', 'Content']};
var arr = [];
({baz: [arr[0], arr[3]], foo: a, bar: b}) = e;
console.log(a + ',' + b + ',' + arr);	// displays: 5,6,Baz,,,Content
[a, b] = [b, a];		// swap contents of a and b
console.log(a + ',' + b);		// displays: 6,5

[a, b, c] = [3, 4, 5]; // permutations
[a, b, c] = [b, c, a];
console.log(a + ',' + b + ',' + c); // displays: 4,5,3
```

#### 擴展/休息運算符

ECMAScript 2015標準引入了「`...`」運算符，用於「擴展語法」(spread syntax)和「剩餘參數」(rest parameters)的相關概念。

擴展語法提供了另一種解構陣列的方法。它表示指定陣列中的元素應用作函數調用中的參數或陣列字面量中的項。

換句話說， "`...`"把"`[...foo]`"變換為"`[foo[0], foo[1], foo[2]]`"，而"`this.bar(...foo);`"變換為"`this.bar(foo[0], foo[1], foo[2]);`"：

```javascript
var a = [1, 2, 3, 4];

// It can be used multiple times in the same expression
var b = [...a, ...a]; // b = [1, 2, 3, 4, 1, 2, 3, 4];

// It can be combined with non-spread items.
var c = [5, 6, ...a, 7, 9]; // c = [5, 6, 1, 2, 3, 4, 7, 9];

// For comparison, doing this without the spread operator 
// creates a nested array.
var d = [a, a]; // d = [[1, 2, 3, 4], [1, 2, 3, 4]]

// It works the same with function calls
function foo(arg1, arg2, arg3) {
    console.log(arg1 + ':' + arg2 + ':' + arg3);
}

// You can use it even if it passes more parameters than the function will use
foo(...a); // "1:2:3" → foo(a[0], a[1], a[2], a[3]);

// You can mix it with non-spread parameters
foo(5, ...a, 6); // "5:1:2" → foo(5, a[0], a[1], a[2], a[3], 6);

// For comparison, doing this without the spread operator
// assigns the array to arg1, and nothing to the other parameters.
foo(a); // "1,2,3,4:undefined:undefined"
```

當在函數聲明中使用`...`時，它表示一個剩餘參數(rest parameter)。剩餘參數必須是函數參數列表中的最右側的命名參數。它將被賦值一個`Array`，其中包含傳遞給函數超過其他命名參數的任何多餘的參數。換句話說，它獲取傳遞給函數的「多餘的」參數（因此得名）。

```javascript
function foo(a, b, ...c) {
    console.log(c.length);
}

foo(1, 2, 3, 4, 5); // "3" → c = [3, 4, 5]
foo('a', 'b'); // "0" → c = []
```

剩餘參數類似於Javascript的 `arguments`對象，它是一個類似陣列的對象，包含當前函數調用中的所有參數（命名和未命名）。然而，與 `arguments`參數不同的是，剩餘參數是真正的`Array`對象，因此可以直接對它們使用`.slice()`和`.sort()`等方法。

`...`運算符只能與`Array`對象一起使用。然而，在未來的ECMAScript標準中，有人提議將其擴展到`Object`。)

### 比較運算符

`==`｜相等

`!=`｜不等

`&gt;`｜大於

`&gt;=`｜大於等於

`&lt;`｜小於

`&lt;=`｜小於等於

`===`｜帶類型檢查的相等比較

`!==`｜不相同

引用對象的變量只有在引用同一個對象時才相等或者相同：

```javascript
var obj1 = {a: 1};
var obj2 = {a: 1};
var obj3 = obj1;
console.log(obj1 == obj2);  //false
console.log(obj3 == obj1);  //true
console.log(obj3 === obj1); //true
```

參見String.

### 邏輯運算符

JavaScript提供4個邏輯運算符：

- 酉運算邏輯非 (`NOT = !a`)
- 二元運算析取 (`OR = a

b`)与合取 (`AND = a && b`)

- 三元條件運算符 (`c ? t : f`)

在邏輯運算上下文中，除下述表達式外，任何表達式的計算結果都為true:

- 字串: `""`, `''`,
- 數: `0`, `-0`, `NaN`,
- 特殊: `null`, `undefined`,
- 布爾: `false`.

布爾函數可用於顯式轉換為基礎類型`Boolean`:

```javascript
// Only empty strings return false
console.log(Boolean("")      === false);
console.log(Boolean("false") === true);
console.log(Boolean("0")     === true);

// Only zero and NaN return false
console.log(Boolean(NaN) === false);
console.log(Boolean(0)   === false);
console.log(Boolean(-0)  === false); // equivalent to -1*0
console.log(Boolean(-2)  === true);

// All objects return true
console.log(Boolean(this) === true);
console.log(Boolean({})   === true);
console.log(Boolean([])   === true);

// These types return false
console.log(Boolean(null)      === false);
console.log(Boolean(undefined) === false); // equivalent to Boolean()
```

The NOT operator evaluates its operand as a Boolean and returns the negation. Using the operator twice in a row, as a double negative, explicitly converts an expression to a primitive of type Boolean:

```javascript
console.log( !0 === Boolean(!0));
console.log(Boolean(!0) === 

1);
console.log(

1 === Boolean(1));
console.log(

0 === Boolean(0));
console.log(Boolean(0) === !1);
console.log(!1 === Boolean(!1));
console.log(!"" === Boolean(!""));
console.log(Boolean(!"") === 

"s");
console.log(

"s" === Boolean("s"));
console.log(

"" === Boolean(""));
console.log(Boolean("") === !"s");	
console.log(!"s" === Boolean(!"s"));
```

三元運算符可用於顯式轉換：

```javascript
console.log([] == false); console.log([] ? true : false); // “truthy”, but the comparison uses [].toString()
console.log([0] == false); console.log([0]? true : false); // [0].toString() == "0"
console.log("0" == false); console.log("0"? true : false); // "0" → 0 ... (0 == 0) ... 0 ← false
console.log([1] == true); console.log([1]? true : false); // [1].toString() == "1"
console.log("1" == true); console.log("1"? true : false); // "1" → 1 ... (1 == 1) ... 1 ← true
console.log([2] != true); console.log([2]? true : false); // [2].toString() == "2"
console.log("2" != true); console.log("2"? true : false); // "2" → 2 ... (2 != 1) ... 1 ← true
```

使用後增量(`i++`)等特性的表達式具有預期的副作用。JavaScript提供了表達式的短路求值；僅當左操作數不足以確定表達式的值時，右操作數才被執行。

```javascript
console.log(a || b);  // When a is true, there is no reason to evaluate b.
console.log(a && b);  // When a is false, there is no reason to evaluate b.
console.log(c ? t : f); // When c is true, there is no reason to evaluate f.
```

In early versions of JavaScript and JScript, the binary logical operators returned a Boolean value (like most C-derived programming languages). However, all contemporary implementations return one of their operands instead:

在JavaScript和JScript的早期版本中，二元邏輯運算符返回一個布爾值（就像大多數從C派生的程式語言一樣）。但是，所有Javascript的當代實現都返回其操作數之一：

```javascript
console.log(a || b); // if a is true, return a, otherwise return b
console.log(a && b); // if a is false, return a, otherwise return b
```

更熟悉C中的行為的程式設計師可能會發現這個特性令人驚訝，但它允許更簡潔地表達模式，如空值結合運算符：

```javascript
var s = t || "(default)"; // assigns t, or the default value, if t is null, empty, etc.
```

### 邏輯賦值

`??=`｜空值結合賦值

`||=`｜邏輯或賦值

`&&=`｜邏輯與賦值

### 按位運算符

`&`｜AND

`^`｜XOR

NOT

`&lt;&lt;`｜左移(右端零填充)

`&gt;&gt;`｜右移 (左端為符號位複製)

`&gt;&gt;&gt;`｜右移(左端零填充)

例如:

```javascript
x=11 & 6;
console.log(x); // 2
```

JavaScript支持酉按位運算符:

`~`｜NOT (按位取反)

### 位操作賦值

`&=`｜and

`^=`｜xor

`&lt;&lt;=`｜左移(右端零填充)

`&gt;&gt;=`｜右移 (左端為符號位複製)

`&gt;&gt;&gt;=`｜右移(左端零填充)

例如:

```javascript
x=7;
console.log(x); // 7
x<<=3;
console.log(x); // 7->14->28->56
```

### 字串

`=`｜賦值

`+`｜連接

`+=`｜連接賦值

例如:

```javascript
str = "ab" + "cd";  // "abcd"
str += "e";      // "abcde"

str2 = "2" + 2;     // "22", not "4" or 4.
```

## 控制結構

### 複合語句

一對大括號 `{&nbsp;}` 和一個封閉的語句序列構成一個複合語句，可以在任何可以使用語句的地方使用。

### If ... else

```javascript
if (expr) {
  //statements;
} else if (expr2) {
  //statements;
} else {
  //statements;
}
```

### 三元調解運算符

條件運算符創建一個表達式，該表達式根據條件計算為兩個表達式之一。這類似於根據條件選擇兩個語句之一執行的「if」語句。即，條件運算符之於表達式就像 if 之於語句。

```javascript
 result = condition ? expression : alternative;
```

等價於：

```javascript
 if (condition) {
  result = expression;
 } else {
  result = alternative;
 }
```

與 if 語句不同，條件運算符不能省略其「else-branch」。

### Switch語句

JavaScript的switch語句如下:

```javascript
 switch (expr) {
  case SOMEVALUE:
   // statements;
   break;
  case ANOTHERVALUE:
   // statements;
   break;
  default:
   // statements;
   break;
 }
```

- `break;` 是可選的；但是，通常需要它，因為否則代碼執行將繼續到下一個案例塊的主體。
- 作為預防措施，在最後一個案例的末尾添加一個 break 語句，以防以後添加其他案例。
- 可用字串字面量作為case值。
- 可以使用表達式代替值。
- 當表達式不匹配任何其他指定的情況時，執行默認情況（可選）。
- 大括號是必需的。

### For循環

```javascript
 for (initial; condition; loop statement) {
  /*
   statements will be executed every time
   the for{} loop cycles, while the
   condition is satisfied
  */
 }
```

或：

```javascript
 for (initial; condition; loop statement(iteration)) // one statement
```

### For ... in loop

```javascript
for (var property_name in some_object) {
  // statements using some_object[property_name];
}
```

- 遍歷對象的所有可枚舉屬性。
- 遍歷陣列的所有使用索引，包括陣列對象的所有使用者定義屬性（如果有）。因此，在遍歷陣列時，最好使用傳統的for循環對索引的所有值循環。
- 不同的Web瀏覽器在 for...in 循環語句反映哪些屬性方面存在差異。理論上，這是由ECMAscript標準定義的稱為「DontEnum」的內部狀態屬性控制的，但實際上，每個瀏覽器在自省期間返回的屬性集略有不同。使用 } 測試給定屬性很有用。因此，使用 } 向陣列原型添加方法可能會導致 `for ... in` 循環遍歷方法的名稱。

### While循環

```javascript
while (condition) {
  statement1;
  statement2;
  statement3;
  ...
}
```

### Do ... while循環

```javascript
do {
  statement1;
  statement2;
  statement3;
  ...
} while (condition);
```

### With

with語句將所有給定對象的屬性和方法添加到以下塊的範圍內，讓它們被引用，就好像它們是局部變量一樣。

```javascript
with (document) {
  var a = getElementById('a');
  var b = getElementById('b');
  var c = getElementById('c');
};
```

- 請注意在每次getElementById調用之前缺少document.。

因為with語句的可用性會阻礙程序性能並且被認為會降低代碼的清晰度（因為任何給定的變量實際上可能是封閉with的屬性），因此在「嚴格模式」中不允許使用此語句 .

### Label

JavaScript在大多數實現中都支持嵌套標籤。可以為break語句標記循環或塊，為`continue`標記循環。雖然 `goto` 是保留字， `goto`在JavaScript中未實現。

```javascript
loop1: for (var a = 0; a < 10; a++) {
  if (a == 4) {
    break loop1; // Stops after the 4th attempt
  }
  console.log('a = ' + a);
  loop2: for (var b = 0; b < 10; ++b) {
    if (b == 3) {
     continue loop2; // Number 3 is skipped
    }
    if (b == 6) {
     continue loop1; // Continues the first loop, 'finished' is not shown
    }
    console.log('b = ' + b);
  }
  console.log('finished');
}
block1: {
  console.log('Hello'); // Displays 'Hello'
  break block1;
  console.log('World'); // Will never get here
}
goto block1; // Parse error.
```

## 函數

函數沒有返回語句的情況下，則返回值undefined。

```javascript+genshitex
function gcd(segmentA, segmentB) {
  var diff = segmentA - segmentB;
  if (diff == 0) 
    return segmentA;
  return diff > 0 ? gcd(segmentB, diff) : gcd(segmentA, -diff);
}
console.log(gcd(60, 40)); // 20

var mygcd = gcd; // mygcd is a reference to the same function as gcd. Note no argument ()s.
console.log(mygcd(60, 40)); // 20
```

函數時頭等對象，可以賦值給其他變量。

調用函數時給出的參數數量不一定與函數定義中的參數數量相對應；在調用時沒有匹配實參的命名形參具有值undefined（可以隱式轉換為 false）。在函數中，參數也可以通過arguments對象訪問；這提供了使用索引訪問所有參數（例如），包括那些超出命名參數數量的參數。（雖然參數列表具有 `.length` 屬性，但它不是Array的實例；它沒有諸如.slice、.sort等）

```javascript
function add7(x, y) {
  if (!y) {
    y = 7;
  }
  console.log(x + y + arguments.length);
};
add7(3); // 11
add7(3, 4); // 9
```

實參為原始值（數字、布爾值、字串）按值傳遞。實參為對象，傳遞的是對象的引用。

```javascript
var obj1 = {a : 1};
var obj2 = {b : 2};
function foo(p) {
  p = obj2; // Ignores actual parameter
  p.b = arguments[1];
}
foo(obj1, 3); // Does not affect obj1 at all. 3 is additional parameter
console.log(obj1.a + " " + obj2.b); // writes 1 3
```

函數可以在其他函數內部聲明，並訪問外部函數的局部變量。此外，即使在外部函數退出後，它們也會通過記住外部函數的局部變量來實現完整的閉包。

```javascript
var v = "Top";
var bar, baz;
function foo() {
  var v = "fud";
  bar = function() { console.log(v) };
  baz = function(x) { v = x; };
}
foo();
baz("Fugly");
bar(); // Fugly (not fud) even though foo() has exited.
console.log(v); // Top
```

函數使用的this不是「這個函數屬於誰」，而是「這個函數這次是怎麼被調用的」。例如：

```javascript
function show() {
  console.log(this);
}

show(); //在浏览器非严格模式下，输出 window；在严格模式下，输出 undefined

const obj = {
  name: "Tom",
  show() {
    console.log(this.name);
  }
};

obj.show();//这里this指向obj，因为是 obj.show() 这种调用形式。

const f = obj.show;
f(); //非严格模式，this可能是window；严格模式，this是undefined 。因为不是obj.show() 

function show() {
  console.log(this.name);
}
const obj = { name: "Alice" };
show.call(obj);  // Alice
show.apply(obj); // Alice
const boundShow = show.bind(obj);
boundShow(); // Alice'''粗体文本'''

function Person(name) {
  this.name = name;
}
const p = new Person("Tom"); //构造函数调用
console.log(p.name); // Tom

const obj = {
  name: "Tom",
  show: () => {
    console.log(this.name);
  }
};
obj.show();//因为箭头函数没有自己的 this，它会捕获外部环境的 this。
```

### 函數的定義方式

JavaScript 里常見的函數定義方式主要有 5 類：

- 函數聲明（Function Declaration）
- 函數表達式（Function Expression）
- 箭頭函數（Arrow Function）
- 對象方法簡寫（Method Syntax）
- 構造器創建函數（new Function）

另外還有一些擴展場景寫法：

- 類中的方法
- 生成器函數 function*
- 異步函數 async function
- 異步箭頭函數 async => {}

#### 函數聲明（Function Declaration）

特點：

- 有函數名
- 可以在定義前調用
- 會被提升（hoisting）

例子：

```javaScript
sayHello(); //提升（hoisting）后被调用

function sayHello() {
  console.log("Hello");
}
```

優點：

- 可讀性強
- 適合定義「明確職責的普通函數」
- 支持提升，書寫順序更靈活
- 調試時函數名清晰

缺點：

- 在塊級作用域中的行為要結合嚴格模式 / 環境理解
- 不適合需要「作為值靈活傳遞」的場景時單獨表達

適用場景：

- 工具函數
- 通用業務函數
- 模塊中的主要函數

#### 函數表達式（Function Expression）

```javaScript
 
const fn = function sayHi() { //函数内部名sayHi可省略。报错、调用栈、调试器里显示函数内部名，易于调试。
                            //函数内部名可用于递归调用自己。
                            //函数内部名只在函数自身内部可见，不能在外部用于调用该函数。不能用于hoist 
  console.log("hi");
};
```

特點：

- 函數作為一個值賦給變量
- 不像函數聲明那樣可在定義前直接使用
- 常配合 const / let 使用

優點：

- 更符合「函數也是值」的思想
- 可以方便地賦值、傳參、返回
- 常用於回調或局部函數定義
- 配合 const 更安全，不易被重新賦值

缺點：

- 不像函數聲明那樣天然提升可直接調用
- 匿名函數調試時有時不如具名函數清晰

適用場景：

- 回調函數
- 條件下定義函數
- 作為變量保存函數

#### 箭頭函數（Arrow Function）

例子：

```javaScript
const add = (a, b) => {
  return a + b;
};

const add = (a, b) => a + b; // 单个表达式可以 简写

const square = x => x * x; //单参数可省略括号

const nums = [1, 2, 3];
const result = nums.map(x => x * 2);//在map/filter/reduce 中非常常用
```

箭頭函數最大的特點不是「寫得短」，而是它沒有自己的`this`，會捕獲外層作用域的`this`

缺點：

- 不能用作構造函數
- 沒有 arguments
- 不適合對象方法中依賴對象自身`this`的場景

#### 對象方法語法（Method Syntax）

用於對象字面量中定義方法，語法更簡潔，方法內部有自己的`this`，指向調用它的對象。`this`行為符合通常對象方法預期。主要適用於對象內部定義，不是通用函數定義形式。

```javaScript
const obj = {
  sayHi() {
    console.log("Hi");
  }
};
//这等价于某种意义上的：
const obj = {
  sayHi: function() {
    console.log("Hi");
  }
};
```

#### new Function()構造函數

運行時動態創建函數，字串會被解析執行。可讀性差、性能較差、有安全風險。類似`eval`。日常開發幾乎不推薦。

```javaScript
const add = new Function("a", "b", "return a + b");
```

#### 異步函數

```javaScript
async function fetchData() {
  return "ok";
}
```

#### 異步箭頭函數

```javaScript
const fetchData = async () => {
  return "ok";
};
```

#### 生成器函數

```javaScript
function* gen() {
  yield 1;
  yield 2;
}
```

#### 類方法

```javaScript
class MyClass {
  sayHi() {
    console.log("Hi");
  }
}
```

## 對象Object

為方便起見，類型通常被細分為「基本」(primitives)和「對象」。對象是具有標識符的實體（它們僅與自身相同）並將屬性名稱映射到值（基於原型編程 術語中的「槽」slot）。對象可以被認為是 關聯數組或散列(hash)，並且通常使用這些數據結構來實現。但是，對象具有其他功能，例如原型鏈(prototype chain)，這是普通關聯數組所沒有的。

JavaScript有幾種內置對象，分別是`Array`、`Boolean`、`Date`、`Function`、`Math`、`Number`、`Object`、`RegExp`和`String`。其他對象是「宿主對象」(host object)，不是由語言定義，而是由運行時環境定義。例如，在瀏覽器中，典型的宿主對象屬於DOM（窗口、表單、連結等）。

### 創建對象

可以使用構造函數或對象字面量創建對象。構造函數可以使用內置的Object函數或自定義函數。按照慣例，構造函數的名稱以大寫字母開頭：

```javascript
// Constructor
var anObject = new Object();

// Object literal
var objectA = {};
var objectA2 = {};  // A != A2, {}s create new objects as copies.
var objectB = {index1: 'value 1', index2: 'value 2'};

// Custom constructor (see below)
```

對象字面量和陣列字面量允許人們輕鬆創建靈活的數據結構：

```javascript
var myStructure = {
  name: {
    first: "Mel",
    last: "Smith"
  },
  age: 33,
  hobbies: ["chess", "jogging"]
};
```

這是 JSON 的基礎，它是一種使用類似JavaScript的語法進行數據交換的簡單表示法。

### 方法

method是一個已分配給對象的屬性名稱的函數。與許多面向對象的語言不同，在與對象相關的JavaScript中，函數定義和方法定義沒有區別。相反，區別發生在函數調用期間。函數可以作為方法調用。

當作為方法調用時，標準局部變量 this 會自動設置為 "" 左側的對象實例。（還有 call 和 apply 方法可以顯式設置 this - 一些包，例如 jQuery 用 this 做不尋常的事情。）

在下面的示例中，Foo被用作構造函數。構造函數沒有什麼特別之處——它只是一個初始化對象的普通函數。當與 關鍵字一起使用時，通常情況下，this 設置為新創建的空白對象。

請注意，在下面的示例中，Foo只是為槽分配值，其中一些值是函數。因此它可以為不同的實例分配不同的功能。此示例中沒有原型設計。

```javascript
function px() { return this.prefix + "X"; }

function Foo(yz) {
  this.prefix = "a-";
  if (yz > 0) {
    this.pyz = function() { return this.prefix + "Y"; };
  } else {
    this.pyz = function() { return this.prefix + "Z"; };
  }
  this.m1 = px;
  return this;
}

var foo1 = new Foo(1);
var foo2 = new Foo(0);
foo2.prefix = "b-";

console.log("foo1/2 " + foo1.pyz() + foo2.pyz());
// foo1/2 a-Y b-Z

foo1.m3 = px; // Assigns the function itself, not its evaluated result, i.e. not px()
var baz = {"prefix": "c-"};
baz.m4 = px; // No need for a constructor to make an object.

console.log("m1/m3/m4 " + foo1.m1() + foo1.m3() + baz.m4());
// m1/m3/m4 a-X a-X c-X

foo1.m2(); // Throws an exception, because foo1.m2 doesn't exist.
```

### 構造函數

構造函數只需將值分配給新創建對象的槽。這些值可以是數據或其他函數。

示例：操作對象：

```javascript
function MyObject(attributeA, attributeB) {
  this.attributeA = attributeA;
  this.attributeB = attributeB;
}

MyObject.staticC = "blue"; // On MyObject Function, not object
console.log(MyObject.staticC); // blue

object = new MyObject('red', 1000);

console.log(object.attributeA); // red
console.log(object["attributeB"]); // 1000

console.log(object.staticC); // undefined
object.attributeC = new Date(); // add a new property

delete object.attributeB; // remove a property of object
console.log(object.attributeB); // undefined

delete object; // remove the whole Object (rarely used)
console.log(object.attributeA); // throws an exception
```

構造函數本身在對象原型的「構造函數」槽中被引用。所以。

```javascript
function Foo() {}
// Use of 'new' sets prototype slots (for example, 
// x = new Foo() would set x's prototype to Foo.prototype,
// and Foo.prototype has a constructor slot pointing back to Foo).
x = new Foo();
// The above is almost equivalent to
y = {};
y.constructor = Foo;
y.constructor();
// Except
x.constructor == y.constructor // true
x instanceof Foo // true
y instanceof Foo // false
// y's prototype is Object.prototype, not
// Foo.prototype, since it was initialised with
// {} instead of new Foo.
// Even though Foo is set to y's constructor slot,
// this is ignored by instanceof - only y's prototype's
// constructor slot is considered.
```

函數本身就是對象，可以用來產生類似於「靜態屬性」（使用 C++/Java 術語）的效果，如下所示。（函數對象還有一個特殊的 `prototype` 屬性，如下面的「繼承」部分所述。）

對象刪除很少使用，因為腳本引擎將垃圾收集不再被引用的對象。

### 繼承

JavaScript以Self的方式通過原型設計(prototyping)支持繼承層次結構。

在以下示例中，Derived類繼承自 Base類。當d創建為Derived時，對Base基礎實例的引用被複製到 d.base。

Derive不包含aBaseFunction的值，因此在訪問aBaseFunction時從aBaseFunction檢索它。這可以通過改變base.aBaseFunction的值來明確，這反映在d.aBaseFunction的值中。

一些實現允許使用__proto__槽顯式訪問或設置原型，如下所示。

```javascript
function Base() {
  this.anOverride = function() { console.log("Base::anOverride()"); };

  this.aBaseFunction = function() { console.log("Base::aBaseFunction()"); };
}

function Derived() {
  this.anOverride = function() { console.log("Derived::anOverride()"); };
}

base = new Base();
Derived.prototype = base; // Must be before new Derived()
Derived.prototype.constructor = Derived; // Required to make `instanceof` work

d = new Derived();    // Copies Derived.prototype to d instance's hidden prototype slot.
d instanceof Derived; // true
d instanceof Base;    // true

base.aBaseFunction = function() { console.log("Base::aNEWBaseFunction()"); }

d.anOverride();    // Derived::anOverride()
d.aBaseFunction(); // Base::aNEWBaseFunction()
console.log(d.aBaseFunction == Derived.prototype.aBaseFunction); // true

console.log(d.__proto__ == base); // true in Mozilla-based implementations and false in many others.
```

下面清楚地顯示了在實例創建時對原型的引用是如何「複製」的，但是對原型的更改會影響引用它的所有實例。

```javascript
function m1() { return "One"; }
function m2() { return "Two"; }
function m3() { return "Three"; }

function Base() {}

Base.prototype.m = m2;
bar = new Base();
console.log("bar.m " + bar.m()); // bar.m Two

function Top() { this.m = m3; }
t = new Top();

foo = new Base();
Base.prototype = t;
// No effect on foo, the *reference* to t is copied.
console.log("foo.m " + foo.m()); // foo.m Two

baz = new Base();
console.log("baz.m " + baz.m()); // baz.m Three

t.m = m1; // Does affect baz, and any other derived classes.
console.log("baz.m1 " + baz.m()); // baz.m1 One
```

在實踐中，使用了這些主題的許多變體，它可能既強大又令人困惑。

## 異常處理

JavaScript 包含一個`try ... catch ... finally`異常處理 語句來處理運行時錯誤。

`try ... catch ... finally`語句捕獲由錯誤或 throw 語句導致的異常。它的語法如下：

```javascript
try {
  // Statements in which exceptions might be thrown
} catch(errorValue) {
  // Statements that execute in the event of an exception
} finally {
  // Statements that execute afterward either way
}
```

最初，try塊中的語句執行。如果拋出異常，腳本的控制流會立即轉移到catch塊中的語句，異常可用作錯誤參數。否則將跳過catch塊。如果catch塊不想處理特定錯誤，它可以 throw(errorValue)。

在任何情況下，finally塊中的語句都會被執行。這可用於釋放資源，儘管記憶體會自動進行垃圾回收。

可以省略catch或finally子句。catch參數是必需的。

Mozilla實現允許多個catch語句，作為對ECMAScript標準的擴展。它們遵循類似於Java中使用的語法：

```javascript
try { statement; }
catch (e if e == "InvalidNameException")  { statement; }
catch (e if e == "InvalidIdException")    { statement; }
catch (e if e == "InvalidEmailException") { statement; }
catch (e)                                 { statement; }
```

在瀏覽器中，onerror比捕獲異常更常用。

```javascript
onerror = function (errorValue, url, lineNr) {...; return true;};
```

## 本地函數與方法

與網頁瀏覽器不相關。

### eval (表達式)

將第一個參數計算為表達式，其中可以包括賦值語句。表達式可以引用函數的局部變量。但是，eval代表了主要的安全風險，因為它允許不良行為者執行任意代碼，因此不鼓勵使用它。

```nodejsrepl
var x = 7;
console.log("val " + eval("x + 2"));
```

## 進一步閱讀

- Danny Goodman: JavaScript Bible, Wiley, John & Sons, .
- David Flanagan, Paula Ferguson: JavaScript: The Definitive Guide, O'Reilly & Associates, .
- Thomas A. Powell, Fritz Schneider: JavaScript: The Complete Reference, McGraw-Hill Companies, .
- Axel Rauschmayer: Speaking JavaScript: An In-Depth Guide for Programmers, 460 pages, O'Reilly Media, 25 February 2014, . ( free online edition )
- Emily Vander Veer: JavaScript For Dummies, 4th Edition, Wiley, .

## 外部連結

- A re-introduction to JavaScript - Mozilla Developer Center
- JavaScript Loops
- ECMAScript standard references: ECMA-262
- Interactive JavaScript Lessons - example-based
- JavaScript on About.com: lessons and explanation
- JavaScript Training
- Mozilla Developer Center Core References for JavaScript versions 1.5 , 1.4, 1.3 and 1.2
- Mozilla JavaScript Language Documentation