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