正则表达式 - JavaScript

正则表达式
=====

**正则表达式**（简称 *regex*）允许开发人员根据模式匹配字符串、提取子匹配信息，或简单地测试字符串是否符合该模式。正则表达式在许多编程语言中都有使用，JavaScript 的语法受 Perl 的启发。

我们建议你阅读正则表达式指南，以全面了解可用的正则表达式语法及其工作原理。

描述
--

*正则表达式*是形式语言理论中的一个重要概念。正则表达式是一种描述可能是无限的字符串集合（称为*语言*）的方法。正则表达式的核心需要具备以下特征：

* 可在语言中使用的一系列*字符*，称为*字母表*。
* *连接*：`ab` 表示“字符 `a` 后跟字符 `b`”。
* *并集*：`a|b` 表示“`a` 或 `b`”。
* *克莱尼星号*（Kleene star）：`a*` 表示“零个或多个 `a` 字符”。

假设有一个有限的字母表（如 26 个英文字母或整个 Unicode 字符集），那么所有正则表达式语言都可以通过上述特征生成。当然，许多模式用这种方式来表达非常繁琐（如“10 位数字”或“一个非空格的字符”），因此 JavaScript 正则表达式包含许多速记符号，下面将一一介绍。

**备注：** 由于存在反向引用 (en-US)（正则表达式必须具有有限状态），JavaScript 正则表达式实际上并不“正则”。不过，反向引用仍然是一个非常有用的特性。

### 创建正则表达式

正则表达式通常是用正斜线（`/`）将模式括起来，以字面量形式创建的：

```js
const regex1 = /ab+c/g;
```

也可以使用 `RegExp()` 构造函数来创建正则表达式：

```js
const regex2 = new RegExp("ab+c", "g");
```

它们在运行时没有区别，但可能会对性能、静态分析性和编写转义字符时的人体工程学问题产生影响。有关详细信息，请参阅 `RegExp` 参考资料。

### 正则表达式标志

标志（flag）是一种特殊参数，可以改变正则表达式的解释方式或与输入文本的交互方式。每个标志都与 `RegExp` 对象上的一个访问器属性相对应。

| 标志 | 描述 | 对应属性 |
| --- | --- | --- |
| `d` | 生成子串匹配的索引。 | `hasIndices` |
| `g` | 全局查找。 | `global` |
| `i` | 忽略大小写查找。 | `ignoreCase` |
| `m` | 允许 `^` 和 `$` 匹配换行符。 | `multiline` |
| `s` | 允许 `.` 匹配换行符。 | `dotAll` |
| `u` | “Unicode”；将模式视为 Unicode 码位序列。 | `unicode` |
| `v` | 升级 `u` 模式，提供更多 Unicode 码特性。 | `unicodeSets` (en-US) |
| `y` | 执行“粘性（sticky）”搜索，从目标字符串的当前位置开始匹配。 | `sticky` |

下面各节按语法性质列出了所有可用的正则表达式语法。

### 断言

断言（assertion）是一种结构，用于测试字符串在指定位置是否满足特定条件，但不消耗字符。断言不能使用量词 (en-US)。

输入边界断言：`^`、`$` (en-US)

断言当前位置是输入的开始或结束，如果设置了 `m` 标志，则断言当前位置是一行的开始或结束。

前瞻断言：`(?=...)`、`(?!...)` (en-US)

断言当前位置是否被某个模式跟随。

后瞻断言：`(?<=...)`、`(?<!..)` (en-US)

断言当前位置是否在某个模式之前。

单词边界断言：`\b`、`\B` (en-US)

断言当前位置是单词边界。

### 原子

原子（atom）是正则表达式的最基本单位。每个原子*消耗*字符串中的一个或多个字符，要么匹配失败，要么允许模式继续匹配下一个原子。

反向引用：`\1`、`\2` (en-US)

匹配先前匹配的、用捕获组捕获的子模式。

捕获组：`(...)` (en-US)

匹配子模式并保存匹配信息。

字符类：`[...]`、`[^...]` (en-US)

匹配字符集中的任何字符。启用 `v` (en-US) 标志后，还可用于匹配长度有限的字符串。

字符类转义：`\d`、`\D`、`\w`、`\W`、`\s`、`\S` (en-US)

匹配预定义字符集中的任何字符。

字符转义：`\n`、`\u{...}` (en-US)

匹配可能无法方便地以字面形式表示的字符。

字面字符：`a`、`b` (en-US)

匹配特定字符。

具名反向引用：`\k<name>` (en-US)

匹配先前匹配的子模式，并使用已命名的捕获组进行捕获。

具名捕获组：`(?<name>...)` (en-US)

匹配子模式并保存匹配信息。以后可以用自定义名称而不是模式中的索引来识别该组。

非捕获分组：`(?:...)` (en-US)

匹配子模式，但不记忆匹配信息。

Unicode 字符类转义：`p{...}`、`\P{...}`

匹配 Unicode 属性指定的字符集。启用 `v` (en-US) 标志后，也可用于匹配长度有限的字符串。

通配符：`.` (en-US)

匹配除行结束符外的任何字符，除非设置了 `s` 标志。

### 其他特性

这些特征本身不指定任何模式，但用于组成模式。

析取（逻辑或）：`|` (en-US)

匹配由 `|` 字符分隔的一组备选字符中的任意一个。

量词：`*`、`+`、`?`、`{n}`、`{n,}`、`{n,m}` (en-US)

匹配原子序列一定次数。

### 转义序列

在正则表达式中，*转义序列*是指任何一种由 `\` 后跟一个或多个字符组成的语法。根据 `\` 后面的字符的不同，它们的作用也大相径庭。下面列出了所有有效的“转义序列”：

| 转义序列 | 跟随 | 含义 |
| --- | --- | --- |
| `\B` | 无 | 非单词边界断言 (en-US) |
| `\D` | 无 | 代表非数字字符的字符类转义 (en-US) |
| `\P` | `{`、Unicode 属性和/或值、`}` | 代表不具有指定 Unicode 类型的 Unicode 字符类转义 |
| `\S` | 无 | 代表非空白字符的字符类转义 (en-US) |
| `\W` | 无 | 代表非单词字符的字符类转义 (en-US) |
| `\b` | 无 | 单词边界断言 (en-US)；属于字符类 (en-US)，代表 U+0008（BACKSPACE） |
| `\c` | 从 `A` 到 `Z` 或从 `a` 到 `z` 的字母 | 代表控制字符的字符转义 (en-US)，其值等于字母的字符值乘以 32 |
| `\d` | 无 | 表示数字字符（`0` 至`9`）的字符类转义 (en-US) |
| `\f` | 无 | 代表 U+000C（FORM FEED）字符的字符转义 (en-US) |
| `\k` | `<`、量词、`>` | 具名反向引用 (en-US) |
| `\n` | 无 | 代表 U+000A（LINE FEED）字符的字符转义 (en-US) |
| `\p` | `{`、Unicode 属性和/或值、`}` | 代表具有指定 Unicode 属性的字符的Unicode 字符类转义 |
| `\q` | `{`、一个字符串、`}` | 仅在 `v` 模式字符类 (en-US)中有效；表示要按字面匹配的字符串 |
| `\r` | 无 | 代表 U+000D（CARRIAGE RETURN）字符的字符转义 (en-US) |
| `\s` | 无 | 代表空白字符的字符类转义 (en-US) |
| `\t` | 无 | 代表 U+0009（CHARACTER TABULATION）字符的字符转义 (en-US) |
| `\u` | 4 个十六进制数字；或 `{`、1 至 6 个十六进制数字、`}` | 代表具有给定码点的字符的字符转义 (en-US) |
| `\v` | 无 | 代表 U+000B（LINE TABULATION）字符的字符转义 (en-US) |
| `\w` | 无 | 代表单字（`A` 到 `Z`、`a` 到 `z`、`0` 到 `9`、`_`）的字符类转义 (en-US) |
| `\x` | 2 个十六进制数字 | 表示具有给定值的字符的字符转义 (en-US) |
| `\0` | 无 | 代表 U+0000（NULL）字符的字符转义 (en-US) |

后跟任何其他数字字符的 `\` 变成传统的八进制转义序列，在 Unicode 感知模式中是禁止的。

此外，`\` 后面还可以跟一些非字母或数字字符，在这种情况下，转义序列总是字符转义 (en-US)，代表转义字符本身：

* `\$`、`\(`、`\)`、`\*`、`\+`、`\.`、`\/`、`\?`、`\[`、`\\`、`\]`、`\^`、`{`、`\|`、`}`：在任何地方都有效
* `\-`：仅在字符类 (en-US)中有效
* `\!`、`\#`、`\%`、`\&`、`\,`、`\:`、`\;`、`\<`、`\=`、`\>`、`\@`、`\``、`\~`：仅在 v-模式字符类 (en-US)中有效

其他 ASCII 字符，即空格字符、`"`、`'`、`_` 和上述未提及的任何字母字符，都不是有效的转义序列。在 Unicode 非感知模式下，不属于上述转义序列的转义序列称为*一致转义序列*（identity escape）：它们代表反斜杠后的字符。例如，`\a` 表示字符 `a`。这种行为限制了在不引起向后兼容性问题的情况下引入新转义序列的能力，因此在 Unicode 感知模式下是禁止的。

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # prod-DecimalEscape |
| ECMAScript Language Specification # prod-Atom |
| ECMAScript Language Specification # prod-CharacterClass |
| ECMAScript Language Specification # prod-CharacterClassEscape |
| ECMAScript Language Specification # prod-CharacterEscape |
| ECMAScript Language Specification # prod-Disjunction |
| ECMAScript Language Specification # prod-Assertion |
| ECMAScript Language Specification # prod-PatternCharacter |
| ECMAScript Language Specification # prod-AtomEscape |
| ECMAScript Language Specification # prod-Quantifier |

浏览器兼容性
------

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

参见
--

* 正则表达式指南
* `RegExp`


严格模式 - JavaScript

严格模式
====

**备注：** 有时你会看到非严格模式，被称为“**sloppy mode**”。这不是一个官方术语，但以防万一，你应该意识到这一点。

ECMAScript 5的**严格模式**是采用具有限制性 JavaScript 变体的一种方式，从而使代码隐式地脱离“马虎模式/稀松模式/懒散模式“（sloppy）模式。严格模式不仅仅是一个子集：它的产生是为了形成与正常代码不同的语义。不支持严格模式与支持严格模式的浏览器在执行严格模式代码时会采用不同行为。所以在没有对运行环境展开**特性测试**来验证对于严格模式相关方面支持的情况下，就算采用了严格模式也不一定会取得预期效果。严格模式代码和非严格模式代码可以共存，因此项目脚本可以渐进式地采用严格模式。严格模式对正常的 JavaScript 语义做了一些更改。

1. 严格模式通过**抛出错误**来消除了一些原有**静默错误**。
2. 严格模式修复了一些导致 JavaScript 引擎难以执行优化的缺陷：有时候，相同的代码，严格模式可以比非严格模式下**运行得更快**。
3. 严格模式**禁用了**在 ECMAScript 的未来版本中可能会定义的一些语法。

如果你想改变你的代码，让其工作在具有限制性 JavaScript 环境中，请参阅转换成严格模式。

调用严格模式
------

严格模式可以应用到整个脚本或个别函数中。不要在封闭大括弧 `{}` 内这样做，在这样的上下文中这么做是没有效果的。在 `eval` 、`Function` 、内联事件处理属性、 `WindowTimers.setTimeout()` (en-US) 方法中传入的脚本字符串，其行为类似于开启了严格模式的一个单独脚本，它们会如预期一样工作。

### 为脚本开启严格模式

为整个脚本文件开启严格模式，需要在所有语句之前放一个特定语句 `"use strict";` （或 `'use strict';`）

```js
// 整个脚本都开启严格模式的语法
"use strict";
var v = "Hi! I'm a strict mode script!";
```

这种语法存在陷阱，有一个大型网站已经被它坑倒了：不能盲目地合并冲突代码。试想合并一个严格模式的脚本和一个非严格模式的脚本：合并后的脚本代码看起来是严格模式。反之亦然：非严格合并严格看起来是非严格的。合并均为严格模式的脚本或均为非严格模式的都没问题，只有在合并严格模式与非严格模式有可能有问题。建议按一个个函数去开启严格模式（至少在学习的过渡期要这样做）.

你也可以将整个脚本的内容用一个函数包括起来，然后在这个外部函数中使用严格模式。这样做就可以消除合并的问题，但是这就意味着你必须要在函数作用域外声明一个全局变量。

### 为函数开启严格模式

同样地，要给某个函数开启严格模式，得把 `"use strict";` (或 `'use strict';`) 声明*一字不漏地*放在函数体所有语句之前。

```js
function strict() {
  // 函数级别严格模式语法
  "use strict";
  function nested() {
    return "And so am I!";
  }
  return "Hi! I'm a strict mode function! " + nested();
}

function notStrict() {
  return "I'm not strict.";
}
```

严格模式中的变化
--------

严格模式同时改变了语法及运行时行为。变化通常分为这几类：将问题直接转化为错误（如语法错误或运行时错误）, 简化了如何为给定名称的特定变量计算，简化了 `eval` 以及 `arguments`, 将写"安全“JavaScript 的步骤变得更简单，以及改变了预测未来 ECMAScript 行为的方式。

### 将过失错误转成异常

在严格模式下，某些先前被接受的过失错误将会被认为是异常。JavaScript 被设计为能使新人开发者更易于上手，所以有时候会给本来错误操作赋予新的不报错误的语义 (non-error semantics). 有时候这可以解决当前的问题，但有时候却会给以后留下更大的问题。严格模式则把这些失误当成错误，以便可以发现并立即将其改正。

第一，严格模式下无法再意外创建全局变量。在普通的 JavaScript 里面给一个错误命名的变量名赋值会使全局对象新增一个属性并继续“工作”（尽管将来可能会失败：在现代的 JavaScript 中有可能）。严格模式中意外创建全局变量被抛出错误替代：

```js
"use strict";
// 假如有一个全局变量叫做 mistypedVariable
mistypedVaraible = 17; // 因为变量名拼写错误
// 这一行代码就会抛出 ReferenceError
```

第二，严格模式会使引起静默失败 (silently fail，注：不报错也没有任何效果) 的赋值操作抛出异常。例如，`NaN` 是一个不可写的全局变量。在正常模式下，给 `NaN` 赋值不会产生任何作用; 开发者也不会受到任何错误反馈。但在严格模式下，给 `NaN` 赋值会抛出一个异常。任何在正常模式下引起静默失败的赋值操作 (给不可写属性赋值，给只读属性 (getter-only) 赋值，给不可扩展对象 (non-extensible object) 的新属性赋值) 都会抛出异常：

```js
"use strict";

// 给不可写属性赋值
var obj1 = {};
Object.defineProperty(obj1, "x", { value: 42, writable: false });
obj1.x = 9; // 抛出 TypeError 错误

// 给只读属性赋值
var obj2 = {
  get x() {
    return 17;
  },
};
obj2.x = 5; // 抛出 TypeError 错误

// 给不可扩展对象的新属性赋值
var fixed = {};
Object.preventExtensions(fixed);
fixed.newProp = "ohai"; // 抛出 TypeError 错误
```

第三，在严格模式下，试图删除不可删除的属性时会抛出异常 (之前这种操作不会产生任何效果):

```js
"use strict";
delete Object.prototype; // 抛出 TypeError 错误
```

第四，在 Gecko 版本 34 之前，严格模式要求一个对象内的所有属性名在对象内必须唯一。正常模式下重名属性是允许的，最后一个重名的属性决定其属性值。因为只有最后一个属性起作用，当代码要去改变属性值而不是修改最后一个重名属性的时候，复制这个对象就产生一连串的 bug。在严格模式下，重名属性被认为是语法错误：

**备注：** 这个问题在 ECMAScript6 中已经不复存在（Firefox bug 1041128）。

```js
"use strict";
var o = { p: 1, p: 2 }; // !!! 语法错误
```

第五，严格模式要求函数的参数名唯一。在正常模式下，最后一个重名参数名会掩盖之前的重名参数。之前的参数仍然可以通过 `arguments[i] 来访问`, 还不是完全无法访问。然而，这种隐藏毫无意义而且可能是意料之外的 (比如它可能本来是打错了), 所以在严格模式下重名参数被认为是语法错误：

```js
function sum(a, a, c) {
  // !!! 语法错误
  "use strict";
  return a + a + c; // 代码运行到这里会出错
}
```

第六，严格模式禁止八进制数字语法。ECMAScript 并不包含八进制语法，但所有的浏览器都支持这种以零 (`0`) 开头的八进制语法：`0644 === 420` 还有 `"\045" === "%"`.在 ECMAScript 6 中支持为一个数字加"`0`o"的前缀来表示八进制数。

```js
var a = 0o10; // ES6: 八进制
```

有些新手开发者认为数字的前导零没有语法意义，所以他们会用作对齐措施 — 但其实这会改变数字的意义！八进制语法很少有用并且可能会错误使用，所以严格模式下八进制语法会引起语法错误：

```js
"use strict";
var sum =
  015 + // !!! 语法错误
  197 +
  142;
```

第七，ECMAScript 6 中的严格模式禁止设置primitive值的属性。不采用严格模式，设置属性将会简单忽略 (no-op),采用严格模式，将抛出`TypeError`错误

```js
"use strict";

false.true = ""; // TypeError
(14).sailing = "home"; // TypeError
"with".you = "far away"; // TypeError
```

### 简化变量的使用

严格模式简化了代码中变量名字映射到变量定义的方式。很多编译器的优化是依赖存储变量 X 位置的能力：这对全面优化 JavaScript 代码至关重要。JavaScript 有些情况会使得代码中名字到变量定义的基本映射只在运行时才产生。严格模式移除了大多数这种情况的发生，所以编译器可以更好的优化严格模式的代码。

第一，严格模式禁用 `with`. `with`所引起的问题是块内的任何名称可以映射 (map) 到 with 传进来的对象的属性，也可以映射到包围这个块的作用域内的变量 (甚至是全局变量), 这一切都是在运行时决定的：在代码运行之前是无法得知的。严格模式下，使用 `with` 会引起语法错误，所以就不会存在 with 块内的变量在运行时才决定引用到哪里的情况了：

```js
"use strict";
var x = 17;
with (obj) {
  // !!! 语法错误
  // 如果没有开启严格模式，with 中的这个 x 会指向 with 上面的那个 x，还是 obj.x？
  // 如果不运行代码，我们无法知道，因此，这种代码让引擎无法进行优化，速度也就会变慢。
  x;
}
```

一种取代 `with`的简单方法是，将目标对象赋给一个短命名变量，然后访问这个变量上的相应属性。

第二，`严格模式下的 eval 不再为上层范围 (surrounding scope，注：包围 eval 代码块的范围) 引入新变量`. 在正常模式下，代码 `eval("var x;")` 会给上层函数 (surrounding function) 或者全局引入一个新的变量 `x` . 这意味着，一般情况下，在一个包含 `eval` 调用的函数内所有没有引用到参数或者局部变量的名称都必须在运行时才能被映射到特定的定义 (因为 `eval` 可能引入的新变量会覆盖它的外层变量). 在严格模式下 `eval` 仅仅为被运行的代码创建变量，所以 `eval` 不会使得名称映射到外部变量或者其他局部变量：

```js
var x = 17;
var evalX = eval("'use strict'; var x = 42; x");
console.assert(x === 17);
console.assert(evalX === 42);
```

相应地，如果函数 `eval` 被在严格模式下的`eval(...)`以表达式的形式调用时，其代码会被当做严格模式下的代码执行。当然也可以在代码中显式开启严格模式，但这样做并不是必须的。

```js
function strict1(str) {
  "use strict";
  return eval(str); // str 中的代码在严格模式下运行
}
function strict2(f, str) {
  "use strict";
  return f(str); // 没有直接调用 eval(...): 当且仅当 str 中的代码开启了严格模式时
  // 才会在严格模式下运行
}
function nonstrict(str) {
  return eval(str); // 当且仅当 str 中的代码开启了"use strict"，str 中的代码才会在严格模式下运行
}

strict1("'Strict mode code!'");
strict1("'use strict'; 'Strict mode code!'");
strict2(eval, "'Non-strict code.'");
strict2(eval, "'use strict'; 'Strict mode code!'");
nonstrict("'Non-strict code.'");
nonstrict("'use strict'; 'Strict mode code!'");
```

因此，在 eval 执行的严格模式代码下，变量的行为与严格模式下非 eval 执行的代码中的变量相同。

第三，严格模式禁止删除声明变量。`delete name` 在严格模式下会引起语法错误：

```js
"use strict";

var x;
delete x; // !!! 语法错误

eval("var y; delete y;"); // !!! 语法错误
```

### 让 eval 和 arguments 变的简单

严格模式让 `arguments` 和 `eval` 少了一些奇怪的行为。两者在通常的代码中都包含了很多奇怪的行为：`eval` 会添加删除绑定，改变绑定好的值，还会通过用它索引过的属性给形参取别名的方式修改形参。虽然在未来的 ECMAScript 版本解决这个问题之前，是不会有补丁来完全修复这个问题，但严格模式下将 eval 和 arguments 作为关键字对于此问题的解决是很有帮助的。

第一，名称 `eval` 和 `arguments` 不能通过程序语法被绑定 (be bound) 或赋值。以下的所有尝试将引起语法错误：

```js
"use strict";
eval = 17;
arguments++;
++eval;
var obj = { set p(arguments) {} };
var eval;
try {
} catch (arguments) {}
function x(eval) {}
function arguments() {}
var y = function eval() {};
var f = new Function("arguments", "'use strict'; return 17;");
```

第二，严格模式下，参数的值不会随 arguments 对象的值的改变而变化。在正常模式下，对于第一个参数是 arg 的函数，对 arg 赋值时会同时赋值给 arguments[`0`]，反之亦然（除非没有参数，或者 arguments[`0`] 被删除）。严格模式下，函数的 arguments 对象会保存函数被调用时的原始参数。arguments[i] 的值不会随与之相应的参数的值的改变而变化，同名参数的值也不会随与之相应的 arguments[i] 的值的改变而变化。

```js
function f(a) {
  "use strict";
  a = 42;
  return [a, arguments[0]];
}
var pair = f(17);
console.assert(pair[0] === 42);
console.assert(pair[1] === 17);
```

第三，不再支持 `arguments.callee`。正常模式下，`arguments.callee` 指向当前正在执行的函数。这个作用很小：直接给执行函数命名就可以了！此外，`arguments.callee` 十分不利于优化，例如内联函数，因为 `arguments.callee` 会依赖对非内联函数的引用。在严格模式下，`arguments.callee` 是一个不可删除属性，而且赋值和读取时都会抛出异常：

```js
"use strict";
var f = function () {
  return arguments.callee;
};
f(); // 抛出类型错误
```

### "安全的" JavaScript

严格模式下更容易写出“安全”的 JavaScript。现在有些网站提供了方式给用户编写能够被网站其他用户执行的 JavaScript 代码。在浏览器环境下，JavaScript 能够获取用户的隐私信息，因此这类 Javascript 必须在运行前部分被转换成需要申请访问禁用功能的权限。没有很多的执行时检查的情况，Javascript 的灵活性让它无法有效率地做这件事。一些语言中的函数普遍出现，以至于执行时检查他们会引起严重的性能损耗。做一些在严格模式下发生的小改动，要求用户提交的 JavaScript 开启严格模式并且用特定的方式调用，就会大大减少在执行时进行检查的必要。

第一，在严格模式下通过`this`传递给一个函数的值不会被强制转换为一个对象。对一个普通的函数来说，`this`总会是一个对象：不管调用时`this`它本来就是一个对象；还是用布尔值，字符串或者数字调用函数时函数里面被封装成对象的`this`；还是使用`undefined`或者`null`调用函数式`this`代表的全局对象（使用`call`, `apply`或者`bind`方法来指定一个确定的`this`）。这种自动转化为对象的过程不仅是一种性能上的损耗，同时在浏览器中暴露出全局对象也会成为安全隐患，因为全局对象提供了访问那些所谓安全的 JavaScript 环境必须限制的功能的途径。所以对于一个开启严格模式的函数，指定的`this`不再被封装为对象，而且如果没有指定`this`的话它值是`undefined`：

```js
"use strict";
function fun() {
  return this;
}
console.assert(fun() === undefined);
console.assert(fun.call(2) === 2);
console.assert(fun.apply(null) === null);
console.assert(fun.call(undefined) === undefined);
console.assert(fun.bind(true)() === true);
```

第二，在严格模式中再也不能通过广泛实现的 ECMAScript 扩展“游走于”JavaScript 的栈中。在普通模式下用这些扩展的话，当一个叫`fun`的函数正在被调用的时候，`fun.caller`是最后一个调用`fun`的函数，而且`fun.arguments`包含调用`fun`时用的形参。这两个扩展接口对于“安全”JavaScript 而言都是有问题的，因为他们允许“安全的”代码访问"专有"函数和他们的（通常是没有经过保护的）形参。如果`fun`在严格模式下，那么`fun.caller`和`fun.arguments`都是不可删除的属性而且在存值、取值时都会报错：

```js
function restricted() {
  "use strict";
  restricted.caller; // 抛出类型错误
  restricted.arguments; // 抛出类型错误
}

function privilegedInvoker() {
  return restricted();
}

privilegedInvoker();
```

第三，严格模式下的`arguments`不会再提供访问与调用这个函数相关的变量的途径。在一些旧时的 ECMAScript 实现中`arguments.caller`曾经是一个对象，里面存储的属性指向那个函数的变量。这是一个安全隐患，因为它通过函数抽象打破了本来被隐藏起来的保留值；它同时也是引起大量优化工作的原因。出于这些原因，现在的浏览器没有实现它。但是因为它这种历史遗留的功能，`arguments.caller`在严格模式下同样是一个不可被删除的属性，在赋值或者取值时会报错：

```js
"use strict";
function fun(a, b) {
  "use strict";
  var v = 12;
  return arguments.caller; // 抛出类型错误
}
fun(1, 2); // 不会暴露 v（或者 a，或者 b）
```

### 为未来的 ECMAScript 版本铺平道路

未来版本的 ECMAScript 很有可能会引入新语法，ECMAScript5 中的严格模式就提早设置了一些限制来减轻之后版本改变产生的影响。如果提早使用了严格模式中的保护机制，那么做出改变就会变得更容易。

第一，在严格模式中一部分字符变成了保留的关键字。这些字符包括`implements`, `interface`, `let`, `package`, `private`, `protected`, `public`, `static`和`yield`。在严格模式下，你不能再用这些名字作为变量名或者形参名。

```js
function package(protected) { // !!!
  "use strict";
  var implements; // !!!

  interface: // !!!
  while (true) {
    break interface; // !!!
  }

  function private() { } // !!!
}
function fun(static) { 'use strict'; } // !!!
```

两个针对 Mozilla 开发的警告：第一，如果你的 JavaScript 版本在 1.7 及以上（你的 chrome 代码或者你正确使用了`<script type="">`）并且开启了严格模式的话，因为`let`和`yield`是最先引入的关键字，所以它们会起作用。但是网络上用`<script src="">`或者`<script>...</script>`加载的代码，`let`或者`yield`都不会作为关键字起作用；第二，尽管 ES5 无条件的保留了`class`, `enum`, `export`, `extends`, `import`和`super`关键字，在 Firefox 5 之前，Mozilla 仅仅在严格模式中保留了它们。

第二，严格模式禁止了不在脚本或者函数层面上的函数声明。在浏览器的普通代码中，在“所有地方”的函数声明都是合法的。这并不在 ES5 规范中（甚至是 ES3）！这是一种针对不同浏览器中不同语义的一种延伸。未来的 ECMAScript 版本很有希望制定一个新的，针对不在脚本或者函数层面进行函数声明的语法。在严格模式下禁止这样的函数声明对于将来 ECMAScript 版本的推出扫清了障碍：

```js
"use strict";
if (true) {
  function f() {} // !!! 语法错误
  f();
}

for (var i = 0; i < 5; i++) {
  function f2() {} // !!! 语法错误
  f2();
}

function baz() {
  // 合法
  function eit() {} // 同样合法
}
```

这种禁止放到严格模式中并不是很合适，因为这样的函数声明方式从 ES5 中延伸出来的。但这是 ECMAScript 委员会推荐的做法，浏览器就实现了这一点。

浏览器的严格模式
--------

主流浏览器现在实现了严格模式。但是不要盲目地依赖它，因为市场上仍然有大量的浏览器版本只部分支持严格模式或者根本就不支持（比如 IE10 之前的版本）。*严格模式改变了语义。*依赖这些改变可能会导致没有实现严格模式的浏览器中出现问题或者错误。谨慎地使用严格模式，通过检测相关代码的功能保证严格模式不出问题。最后，记得*在支持或者不支持严格模式的浏览器中测试你的代码*。如果你只在不支持严格模式的浏览器中测试，那么在支持的浏览器中就很有可能出问题，反之亦然。

参见
--

* JavaScript 模块指南
* 词法语法


JavaScript 标准内置对象 - JavaScript

JavaScript 标准内置对象
=================

本章介绍和说明了 JavaScript 中所有的标准内置对象、以及它们的方法和属性。

这里的术语“全局对象”（或标准内置对象）不应与 **global 对象**混淆。这里的“全局对象”指的是**处在全局作用域里的多个对象**。

**global 对象**可以在全局作用域里通过使用 `this` 访问到（但只有在 ECMAScript 5 的非严格模式下才可以，在严格模式下得到的是 `undefined`）。其实全局作用域**包含**全局对象中的属性，包括它可能继承来的属性。

全局作用域中的其他对象则可由用户的脚本创建，或由宿主程序提供。浏览器环境中所提供的宿主对象的说明可以在这里找到：API 参考。

要了解关于 DOM 和核心 JavaScript 之间区别的更多信息，可参阅 JavaScript 技术概述。

标准内置对象分类
--------

### 值属性

这些全局属性返回一个简单值，这些值没有自己的属性和方法。

* `globalThis`
* `Infinity`
* `NaN`
* `undefined`
### 函数属性

全局函数可以直接调用，不需要在调用时指定所属对象，执行结束后会将结果直接返回给调用者。

* `eval()`
* `isFinite()`
* `isNaN()`
* `parseFloat()`
* `parseInt()`
* `decodeURI()`
* `decodeURIComponent()`
* `encodeURI()`
* `encodeURIComponent()`
* `escape()`
已弃用
* `unescape()`
已弃用
### 基本对象

基本对象是定义或使用其他对象的基础。

* `Object`
* `Function`
* `Boolean`
* `Symbol`

#### 错误对象

错误对象是一种特殊的基本对象。它们拥有基本的 `Error` 类型，同时也有多种具体的错误类型。

* `Error`
* `AggregateError`
* `EvalError`
* `RangeError`
* `ReferenceError`
* `SyntaxError`
* `TypeError`
* `URIError`
* `InternalError`
非标准
### 数字和日期对象

用来表示数字、日期和执行数学计算的对象。

* `Number`
* `BigInt`
* `Math`
* `Date`
### 字符串

这些对象表示字符串并支持操作字符串。

* `String`
* `RegExp`
### 可索引的集合对象

这些对象表示按照索引值来排序的数据集合，包括数组和类型数组，以及类数组结构的对象。

* `Array`
* `Int8Array`
* `Uint8Array`
* `Uint8ClampedArray`
* `Int16Array`
* `Uint16Array`
* `Int32Array`
* `Uint32Array`
* `BigInt64Array`
* `BigUint64Array` (en-US)
* `Float32Array`
* `Float64Array`
### 使用键的集合对象

这些集合对象在存储数据时会使用到键，包括可迭代的 `Map` 和 `Set`，支持按照插入顺序来迭代元素。

* `Map`
* `Set`
* `WeakMap`
* `WeakSet`
### 结构化数据

这些对象用来表示和操作结构化的缓冲区数据，或使用 JSON（JavaScript Object Notation）编码的数据。

* `ArrayBuffer`
* `SharedArrayBuffer`
* `Atomics`
* `DataView`
* `JSON`
### 内存管理对象

这些对象会与垃圾回收机制产生交互。

* `WeakRef`
* `FinalizationRegistry` (en-US)
### 控制抽象对象

控件抽象对象可以帮助构造代码，尤其是异步代码（例如不使用深度嵌套的回调）。

* `Iterator`
* `AsyncIterator`
* `Promise`
* `GeneratorFunction`
* `AsyncGeneratorFunction`
* `Generator`
* `AsyncGenerator`
* `AsyncFunction`
### 反射

* `Reflect`
* `Proxy`
### 国际化

ECMAScript 核心的附加功能，用于支持多语言处理。

* `Intl`
* `Intl.Collator`
* `Intl.DateTimeFormat`
* `Intl.DisplayNames`
* `Intl.DurationFormat` (en-US)
* `Intl.ListFormat`
* `Intl.Locale`
* `Intl.NumberFormat`
* `Intl.PluralRules`
* `Intl.RelativeTimeFormat`
* `Intl.Segmenter`


迭代协议 - JavaScript

迭代协议
====

**迭代协议**并不是新的内置实现或语法，而是*协议*。这些协议可以被任何遵循某些约定的对象来实现。

迭代协议具体分为两个协议：可迭代协议和迭代器协议。

可迭代协议
-----

**可迭代协议**允许 JavaScript 对象定义或定制它们的迭代行为，例如，在一个 `for..of` 结构中，哪些值可以被遍历到。一些内置类型同时是内置的可迭代对象，并且有默认的迭代行为，比如 `Array` 或者 `Map`，而其他内置类型则不是（比如 `Object`）。

要成为**可迭代**对象，该对象必须实现 **`@@iterator`** 方法，这意味着对象（或者它原型链上的某个对象）必须有一个键为 `@@iterator` 的属性，可通过常量 `Symbol.iterator` 访问该属性：

`[Symbol.iterator]`

一个无参数的函数，其返回值为一个符合迭代器协议的对象。

当一个对象需要被迭代的时候（比如被置入一个 `for...of` 循环时），首先，会不带参数调用它的 `@@iterator` 方法，然后使用此方法返回的**迭代器**获得要迭代的值。

值得注意的是调用此无参数函数时，它将作为对可迭代对象的方法进行调用。因此，在函数内部，`this` 关键字可用于访问可迭代对象的属性，以决定在迭代过程中提供什么。

此函数可以是普通函数，也可以是生成器函数，以便在调用时返回迭代器对象。在此生成器函数的内部，可以使用 `yield` 提供每个条目。

迭代器协议
-----

**迭代器协议**定义了产生一系列值（无论是有限个还是无限个）的标准方式，当值为有限个时，所有的值都被迭代完毕后，则会返回一个默认返回值。

只有实现了一个拥有以下语义（semantic）的 **`next()`** 方法，一个对象才能成为迭代器：

`next()`

无参数或者接受一个参数的函数，并返回符合 `IteratorResult` 接口的对象（见下文）。如果在使用迭代器内置的语言特征（例如 `for...of`）时，得到一个非对象返回值（例如 `false` 或 `undefined`），将会抛出 `TypeError`（`"iterator.next() returned a non-object value"`）。

所有迭代器协议的方法（`next()`、`return()` 和 `throw()`）都应返回实现 `IteratorResult` 接口的对象。它必须有以下属性：

`done` 可选

如果迭代器能够生成序列中的下一个值，则返回 `false` 布尔值。（这等价于没有指定 `done` 这个属性。）

如果迭代器已将序列迭代完毕，则为 `true`。这种情况下，`value` 是可选的，如果它依然存在，即为迭代结束之后的默认返回值。

`value` 可选

迭代器返回的任何 JavaScript 值。`done` 为 `true` 时可省略。

实际上，两者都不是严格要求的；如果返回没有任何属性的对象，则实际上等价于 `{ done: false, value: undefined }`。

如果一个迭代器返回一个 `done: true` 的结果，则对任何 `next()` 的后续调用也返回 `done: true`，尽管这在语言层面不是强制的。

`next` 方法可以接受一个值，该值将提供给方法体。任何内置的语言特征都将不会传递任何值。传递给生成器 `next` 方法的值将成为相应 `yield` 表达式的值。

可选地，迭代器也实现了 **`return(value)`** 和 **`throw(exception)`** 方法，这些方法在调用时告诉迭代器，调用者已经完成迭代，并且可以执行任何必要的清理（例如关闭数据库连接）。

`return(value)` 可选

无参数或者接受一个参数的函数，并返回符合 `IteratorResult` 接口的对象，其 `value` 通常等价于传递的 `value`，并且 `done` 等于 `true`。调用这个方法表明迭代器的调用者不打算调用更多的 `next()`，并且可以进行清理工作。

`throw(exception)` 可选

无参数或者接受一个参数的函数，并返回符合 `IteratorResult` 接口的对象，通常 `done` 等于 `true`。调用这个方法表明迭代器的调用者监测到错误的状况，并且 `exception` 通常是一个 `Error` 实例。

**备注：** 无法通过反射的方法确定（例如，没有实际调用 `next()` 并验证返回的结果）一个特定的对象是否实现了迭代器协议。

很容易使一个迭代器也可迭代：只需实现 `[@@iterator]()` 方法，并返回它的 `this`。

```js
// Satisfies both the Iterator Protocol and Iterable
const myIterator = {
  next() {
    // ...
  },
  [Symbol.iterator]() {
    return this;
  },
};
```

这种对象被称为*可迭代迭代器*。这样做可以让期望可迭代对象的各类语法使用此类迭代器——因此，在不实现可迭代协议的情况下，仅实现迭代器协议的作用很小。（事实上，几乎所有的语法和 API 都期待*可迭代对象，而不是迭代器*。）生成器对象是一个例子：

```js
const aGeneratorObject = (function\* () {
  yield 1;
  yield 2;
  yield 3;
})();

console.log(typeof aGeneratorObject.next);
// "function"——它有 next 方法（返回正确的值），所以它是迭代器

console.log(typeof aGeneratorObject[Symbol.iterator]);
// "function"——它有 @@iterator 方法（返回正确的迭代器），所以它是可迭代的

console.log(aGeneratorObject[Symbol.iterator]() === aGeneratorObject);
// true——它的 @@iterator 方法返回自身（一个迭代器），所以它是一个可迭代的迭代器
```

然而，可能的情况下，`iterable[Symbol.iterator]` 最好返回总是从头开始的不同的迭代器，像 `Set.prototype[@@iterator]()` 做的那样。

异步迭代器和异步可迭代协议
-------------

还有一对用于异步迭代的协议，命名为**异步迭代器**和**异步可迭代**协议。它们与可迭代和迭代器协议有着非常类似的接口，只是从调用迭代器方法的每个返回值都包装在一个 promise 中。

当对象实现以下方法时，它会实现异步可迭代协议：

`[Symbol.asyncIterator]`

返回对象的无参数函数，并且符合异步迭代器协议。

当对象实现以下方法时，它会实现异步迭代器协议：

`next()`

无参数或者接受一个参数的函数，并返回 promise。promise 兑现为一个对象，该对象符合 `IteratorResult` 接口，并且这些属性与同步迭代器有着相同的语义。

`return(value)` 可选

无参数或者接受一个参数的函数，并返回 promise。promise 兑现为一个对象，该对象符合 `IteratorResult` 接口，并且这些属性与同步迭代器有着相同的语义。

`throw(exception)` 可选

无参数或者接受一个参数的函数，并返回 promise。promise 兑现为一个对象，该对象符合 `IteratorResult` 接口，并且这些属性与同步迭代器有着相同的语义。

语言和迭代协议之间的交互
------------

JavaScript 语言指定了产生或使用可迭代对象和迭代器的 API。

### 内置的可迭代对象

`String`、`Array`、`TypedArray`、`Map`、`Set` 以及 `Intl.Segments` (en-US) 都是内置的可迭代对象，因为它们的每个 `prototype` 对象都实现了 `@@iterator` 方法。此外，`arguments` 对象和一些 DOM 集合类型，如 `NodeList` 也是可迭代的。目前，没有内置的异步可迭代对象。

生成器函数返回生成器对象，它们是可迭代的迭代器。异步生成器函数返回异步生成器对象，它们是异步可迭代的迭代器。

从内置迭代返回的迭代器实际上都继承了一个公共类（目前尚未暴露），该类实现了上述 `[Symbol.iterator]() { return this; }` 方法，使它们都是可迭代的迭代器。将来，除了迭代器协议要求的 `next()` 方法外，这些内置迭代器可能还有其他辅助方法。你可以通过在图形控制台中记录迭代器的原型链来检查它。
```

console.log([][Symbol.iterator]());

Array Iterator {}
  [[Prototype]]: Array Iterator     ==> This is the prototype shared by all array iterators
    next: ƒ next()
    Symbol(Symbol.toStringTag): "Array Iterator"
    [[Prototype]]: Object           ==> This is the prototype shared by all built-in iterators
      Symbol(Symbol.iterator): ƒ [Symbol.iterator]()
      [[Prototype]]: Object         ==> This is Object.prototype
```

### 接受可迭代对象的内置 API

有许多 API 接受可迭代对象。一些例子，包括：

* `Map()`
* `WeakMap()`
* `Set()`
* `WeakSet()`
* `Promise.all()`
* `Promise.allSettled()`
* `Promise.race()`
* `Promise.any()`
* `Array.from()`
* `Object.groupBy()`
* `Map.groupBy()`

```js
const myObj = {};

new WeakSet(
  (function\* () {
    yield {};
    yield myObj;
    yield {};
  })(),
).has(myObj); // true
```

### 期待迭代对象的语法

一些语句和表达式期望可迭代对象，例如 `for...of` 循环、数组和参数扩展、`yield*` 和数组解构：

```js
for (const value of ["a", "b", "c"]) {
  console.log(value);
}
// "a"
// "b"
// "c"

console.log([..."abc"]); // ["a", "b", "c"]

function\* gen() {
  yield\* ["a", "b", "c"];
}

console.log(gen().next()); // { value: "a", done: false }

[a, b, c] = new Set(["a", "b", "c"]);
console.log(a); // "a"
```

当内置语法迭代迭代器，并且最后的结果中 `done` 为 `false`（即迭代器能够生成更多值）但不需要更多值时，如果存在，将调用 `return` 方法。例如，如果在 `for...of` 循环中遇到 `break` 或 `return`，或者数组解构中的标识符只是有限个的，则可能会发生这种情况。

```js
const obj = {
  [Symbol.iterator]() {
    let i = 0;
    return {
      next() {
        i++;
        console.log("Returning", i);
        if (i === 3) return { done: true, value: i };
        return { done: false, value: i };
      },
      return() {
        console.log("Closing");
        return { done: true };
      },
    };
  },
};

const [b] = obj;
// Returning 1
// Closing

const [a, b, c] = obj;
// Returning 1
// Returning 2
// Returning 3
// Already reached the end (the last call returned `done: true`),
// so `return` is not called

for (const b of obj) {
  break;
}
// Returning 1
// Closing
```

异步生成器函数（但不是同步生成器函数）中的 `for await...of` 循环和 `yield*` 是与异步迭代交互的唯一方式。在不是同步迭代的异步迭代对象（即它有 `[@@asyncIterator]()` 但没有 `[@@iterator]()`）上使用 `for...of`、数组展开等将抛出 `TypeError：x is not iterable`。

### 不符合标准的可迭代对象

如果一个可迭代对象的 `@@iterator` 方法不能返回迭代器对象，那么可以认为它是一个*不符合标准的*（Non-well-formed）可迭代对象。

使用这样的可迭代对象很可能会导致如下的运行时异常，或者不可预料的表现：

```js
const nonWellFormedIterable = {};
nonWellFormedIterable[Symbol.iterator] = () => 1;
[...nonWellFormedIterable]; // TypeError: [Symbol.iterator]() returned a non-object value
```

示例
--

### 自定义可迭代对象

你可以像这样创建自己的可迭代对象：

```js
const myIterable = {
  \*[Symbol.iterator]() {
    yield 1;
    yield 2;
    yield 3;
  },
};

console.log([...myIterable]); // [1, 2, 3]
```

### 简单迭代器

迭代器本质上是有状态的。如果你不将其定义为生成器函数（如上例所示），你可能需要将状态封装在闭包中。

```js
function makeIterator(array) {
  let nextIndex = 0;
  return {
    next() {
      return nextIndex < array.length
        ? {
            value: array[nextIndex++],
            done: false,
          }
        : {
            done: true,
          };
    },
  };
}

const it = makeIterator(["yo", "ya"]);

console.log(it.next().value); // 'yo'
console.log(it.next().value); // 'ya'
console.log(it.next().done); // true
```

### 无穷迭代器

```js
function idMaker() {
  let index = 0;
  return {
    next() {
      return {
        value: index++,
        done: false,
      };
    },
  };
}

const it = idMaker();

console.log(it.next().value); // 0
console.log(it.next().value); // 1
console.log(it.next().value); // 2
// ...
```

### 使用生成器定义一个可迭代对象

```js
function\* makeSimpleGenerator(array) {
  let nextIndex = 0;
  while (nextIndex < array.length) {
    yield array[nextIndex++];
  }
}

const gen = makeSimpleGenerator(["yo", "ya"]);

console.log(gen.next().value); // 'yo'
console.log(gen.next().value); // 'ya'
console.log(gen.next().done); // true

function\* idMaker() {
  let index = 0;
  while (true) {
    yield index++;
  }
}

const it = idMaker();

console.log(it.next().value); // 0
console.log(it.next().value); // 1
console.log(it.next().value); // 2
// ...
```

### 使用类定义一个可迭代对象

状态封装也可以对私有属性进行。

```js
class SimpleClass {
  #data;

  constructor(data) {
    this.#data = data;
  }

  [Symbol.iterator]() {
    // Use a new index for each iterator. This makes multiple
    // iterations over the iterable safe for non-trivial cases,
    // such as use of break or nested looping over the same iterable.
    let index = 0;

    return {
      // Note: using an arrow function allows `this` to point to the
      // one of `[@@iterator]()` instead of `next()`
      next: () => {
        if (index < this.#data.length) {
          return { value: this.#data[index++], done: false };
        } else {
          return { done: true };
        }
      },
    };
  }
}

const simple = new SimpleClass([1, 2, 3, 4, 5]);

for (const val of simple) {
  console.log(val); // 1 2 3 4 5
}
```

### 重写内置的可迭代对象

例如，`String` 是一个内置的可迭代对象：

```js
const someString = "hi";
console.log(typeof someString[Symbol.iterator]); // "function"
```

`String` 的默认迭代器会逐个地返回字符串的代码点：

```js
const iterator = someString[Symbol.iterator]();
console.log(`${iterator}`); // "[object String Iterator]"
console.log(iterator.next()); // { value: "h", done: false }
console.log(iterator.next()); // { value: "i", done: false }
console.log(iterator.next()); // { value: undefined, done: true }
```

你可以提供我们自己的 `@@iterator` 重新定义迭代行为：

```js
// need to construct a String object explicitly to avoid auto-boxing
const someString = new String("hi");

someString[Symbol.iterator] = function () {
  return {
    // this is the iterator object, returning a single element (the string "bye")
    next() {
      return this._first
        ? { value: "bye", done: (this._first = false) }
        : { done: true };
    },
    \_first: true,
  };
};
```

注意，如何使用迭代协议重新定义 `@@iterator` 以影响内置结构的行为：

```js
console.log([...someString]); // ["bye"]
console.log(`${someString}`); // "hi"
```

参见
--

* `function*` 声明
* 在 ECMAScript 规范中的迭代


类 - JavaScript

类
=

Baseline Widely available
-------------------------

This feature is well established and works across many devices and browser versions. It’s been available across browsers since March 2017.

* Learn more
* See full compatibility
* Report feedback
类是用于创建对象的模板。他们用代码封装数据以处理该数据。JS 中的类建立在原型上，但也具有某些语法和语义未与 ES5 类相似语义共享。

定义类
---

实际上，类是“特殊的函数”，就像你能够定义的函数表达式和函数声明一样，类语法有两个组成部分：类表达式和类声明。

### 类声明

定义类的一种方法是使用**类声明**。要声明一个类，你可以使用带有`class`关键字的类名（这里是“Rectangle”）。

```js
class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}
```

#### 提升

**函数声明**和**类声明**之间的一个重要区别在于，函数声明会提升，类声明不会。你首先需要声明你的类，然后再访问它，否则类似以下的代码将抛出`ReferenceError`：

```js
let p = new Rectangle(); // ReferenceError

class Rectangle {}
```

### 类表达式

**类表达式**是定义类的另一种方法。类表达式可以命名或不命名。命名类表达式的名称是该类体的局部名称。(不过，可以通过类的 (而不是一个实例的) `name` 属性来检索它)。

```js
// 未命名/匿名类
let Rectangle = class {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
};
console.log(Rectangle.name);
// output: "Rectangle"

// 命名类
let Rectangle = class Rectangle2 {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
};
console.log(Rectangle.name);
// 输出："Rectangle2"
```

**备注：** 类**表达式**也同样受到类声明部分中提到的类型提升的限制。

类体和方法定义
-------

一个类的类体是一对花括号/大括号 `{}` 中的部分。这是你定义类成员的位置，如方法或构造函数。

### 严格模式

类声明和类表达式的主体都执行在严格模式下。比如，构造函数，静态方法，原型方法，getter 和 setter 都在严格模式下执行。

### 构造函数

constructor方法是一个特殊的方法，这种方法用于创建和初始化一个由`class`创建的对象。一个类只能拥有一个名为“constructor”的特殊方法。如果类包含多个`constructor`的方法，则将抛出 一个`SyntaxError` 。

一个构造函数可以使用 `super` 关键字来调用一个父类的构造函数。

### 原型方法

参见方法定义。

```js
class Rectangle {
  // constructor
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
  // Getter
  get area() {
    return this.calcArea();
  }
  // Method
  calcArea() {
    return this.height \* this.width;
  }
}
const square = new Rectangle(10, 10);

console.log(square.area);
// 100
```

### 静态方法

`static` 关键字用来定义一个类的一个静态方法。调用静态方法不需要实例化 (en-US) 该类，但不能通过一个类实例调用静态方法。静态方法通常用于为一个应用程序创建工具函数。

```js
class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  static displayName = "Point";

  static distance(a, b) {
    const dx = a.x - b.x;
    const dy = a.y - b.y;
    return Math.hypot(dx, dy);
  }
}

const p1 = new Point(5, 5);
const p2 = new Point(10, 10);
p1.displayName;
// undefined
p1.distance;
// undefined

console.log(Point.displayName);
// "Point"
console.log(Point.distance(p1, p2));
// 7.0710678118654755
```

### 用原型和静态方法绑定 this

当调用静态或原型方法时没有指定 *this* 的值，那么方法内的 *this* 值将被置为 **`undefined`**。即使你未设置 `"use strict"` ，因为 `class` 体内部的代码总是在严格模式下执行。

```js
class Animal {
  speak() {
    return this;
  }
  static eat() {
    return this;
  }
}

let obj = new Animal();
obj.speak(); // Animal {}
let speak = obj.speak;
speak(); // undefined

Animal.eat(); // class Animal
let eat = Animal.eat;
eat(); // undefined
```

如果上述代码通过传统的基于函数的语法来实现，那么依据初始的 *this* 值，在非严格模式下方法调用会发生自动装箱。若初始值是 `undefined`，*this* 值会被设为全局对象。

严格模式下不会发生自动装箱，*this* 值将保留传入状态。

```js
function Animal() {}

Animal.prototype.speak = function () {
  return this;
};

Animal.eat = function () {
  return this;
};

let obj = new Animal();
let speak = obj.speak;
speak(); // global object

let eat = Animal.eat;
eat(); // global object
```

### 实例属性

实例的属性必须定义在类的方法里：
```

class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}
```

静态的或原型的数据属性必须定义在类定义的外面。
```

Rectangle.staticWidth = 20;
Rectangle.prototype.prototypeWidth = 25;
```

### 字段声明

**警告：** 公共和私有字段声明是 JavaScript 标准委员会TC39提出的实验性功能（第 3 阶段）。浏览器中的支持是有限的，但是可以通过Babel等系统构建后使用此功能。

#### 公有字段声明

使用 JavaScript 字段声明语法，上面的示例可以写成：
```

class Rectangle {
  height = 0;
  width;
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}
```

通过预先声明字段，类定义变得更加自我记录，并且字段始终存在。

在声明字段时，我们不需要像 `let`、`const` 和 `var` 这样的关键字。

正如上面看到的，这个字段可以用也可以不用默认值来声明。

#### 私有字段声明

使用私有字段，可以按以下方式细化定义。
```

class Rectangle {
  #height = 0;
  #width;
  constructor(height, width) {
    this.#height = height;
    this.#width = width;
  }
}
```

从类外部引用私有字段是错误的。它们只能在类里面中读取或写入。通过定义在类外部不可见的内容，可以确保类的用户不会依赖于内部，因为内部可能在不同版本之间发生变化。

**备注：** 私有字段仅能在字段声明中预先定义。

私有字段不能通过在之后赋值来创建它们，这种方式只适用普通属性。

更多信息，请看class fields (en-US).

使用 `extends` 扩展子类
-----------------

`extends` 关键字在 *类声明* 或 *类表达式* 中用于创建一个类作为另一个类的一个子类。

```js
class Animal {
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(`${this.name} makes a noise.`);
  }
}

class Dog extends Animal {
  constructor(name) {
    super(name); // 调用超类构造函数并传入 name 参数
  }

  speak() {
    console.log(`${this.name} barks.`);
  }
}

var d = new Dog("Mitzie");
d.speak(); // 'Mitzie barks.'
```

如果子类中定义了构造函数，那么它必须先调用 `super()` 才能使用 `this` 。

也可以继承传统的基于函数的“类”：

```js
function Animal(name) {
  this.name = name;
}
Animal.prototype.speak = function () {
  console.log(this.name + " makes a noise.");
};

class Dog extends Animal {
  speak() {
    super.speak();
    console.log(this.name + " barks.");
  }
}

var d = new Dog("Mitzie");
d.speak(); //Mitzie makes a noise. Mitzie barks.
```

请注意，类不能继承常规对象（不可构造的）。如果要继承常规对象，可以改用`Object.setPrototypeOf()`：

```js
var Animal = {
  speak() {
    console.log(this.name + " makes a noise.");
  },
};

class Dog {
  constructor(name) {
    this.name = name;
  }
}

Object.setPrototypeOf(Dog.prototype, Animal); // 如果不这样做，在调用 speak 时会返回 TypeError

var d = new Dog("Mitzie");
d.speak(); // Mitzie makes a noise.
```

Species
-------

你可能希望在派生数组类 \*`MyArray` \*中返回 `Array`对象。这种 species 方式允许你覆盖默认的构造函数。

例如，当使用像`map()`返回默认构造函数的方法时，你希望这些方法返回一个父`Array`对象，而不是`MyArray`对象。`Symbol.species` 符号可以让你这样做：

```js
class MyArray extends Array {
  // Overwrite species to the parent Array constructor
  static get [Symbol.species]() {
    return Array;
  }
}
var a = new MyArray(1, 2, 3);
var mapped = a.map((x) => x \* x);

console.log(mapped instanceof MyArray);
// false
console.log(mapped instanceof Array);
// true
```

使用 `super` 调用超类
---------------

`super` 关键字用于调用对象的父对象上的函数。

```js
class Cat {
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(this.name + " makes a noise.");
  }
}

class Lion extends Cat {
  speak() {
    super.speak();
    console.log(this.name + " roars.");
  }
}
```

Mix-ins / 混入
------------

抽象子类或者 mix-ins 是类的模板。一个 ECMAScript 类只能有一个单超类，所以想要从工具类来多重继承的行为是不可能的。子类继承的只能是父类提供的功能性。因此，例如，从工具类的多重继承是不可能的。该功能必须由超类提供。

一个以超类作为输入的函数和一个继承该超类的子类作为输出可以用于在 ECMAScript 中实现混合：

```js
var calculatorMixin = (Base) =>
  class extends Base {
    calc() {}
  };

var randomizerMixin = (Base) =>
  class extends Base {
    randomize() {}
  };
```

使用 mix-ins 的类可以像下面这样写：

```js
class Foo {}
class Bar extends calculatorMixin(randomizerMixin(Foo)) {}
```

规范
--

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

浏览器兼容性
------

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

重新运行一个类定义
---------

无法重新定义类。尝试这样做会产生一个 `SyntaxError`.

如果你正在使用 Web 浏览器（例如 Firefox Web 控制台， (**Tools** > **Web Developer** > **Web Console**) 并且你两次“运行”具有相同名称的类的定义，你将收到一个 `SyntaxError: redeclaration of let ClassName;`. (请参阅中有关此问题的进一步讨论 Firefox bug 1428672.) 在 Chrome 开发者工具中执行类似的操作会给你一个以下信息： `Uncaught SyntaxError: Identifier 'ClassName' has already been declared at <anonymous>:1:1`.

参见
--

* 函数
* 类声明
* 类表达式
* 公有类字段
* 私有属性
* `super`
* Blog post: "ES6 In Depth: Classes"
* Fields and public/private class properties proposal (stage 3)


尾后逗号 - JavaScript

尾后逗号
====

**尾后逗号** （有时叫做“终止逗号”）在向 JavaScript 代码添加元素、参数、属性时十分有用。如果你想要添加新的属性，并且上一行已经使用了尾后逗号，你可以仅仅添加新的一行，而不需要修改上一行。这使得版本控制的代码比较（diff）更加清晰，代码编辑过程中遇到的麻烦更少。

JavaScript 一开始就支持数组字面量中的尾后逗号，随后向对象字面量（ECMAScript 5）中添加了尾后逗号。最近（ECMAScript 2017），又将其添加到函数参数中。

但是，JSON 不允许尾后逗号。

字面量中的尾后逗号
---------

### 数组

JavaScript 忽略数组中的尾后逗号：

```js
const arr = [
  1,
  2,
  3,
];

arr; // [1, 2, 3]
arr.length; // 3
```

如果使用了多于一个尾后逗号，会产生省略（elision，或者间隙 hole）。带有间隙的数组叫做*稀疏*数组（*sparse* 紧凑数组 *dense* array 没有省略/间隙）。例如，当使用 `Array.prototype.forEach()` 或 `Array.prototype.map()` 迭代数组时，会跳过数组间隙。

```js
const arr = [1, 2, 3, , ,];
arr.length; // 5
```

### 对象

从 ECMAScript 5 开始，对象字面值中的尾后逗号也是符合语法的：

```js
const object = {
  foo: "bar",
  baz: "qwerty",
  age: 42,
};
```

函数中的尾后逗号
--------

ECMAScript 2017 支持函数参数中的尾后逗号。

### 参数定义

下面的两个函数定义都是合法的，并且互相等价。尾后逗号并不影响函数定义，或者其 `arguments` 对象的 `length` 属性。

```js
function f(p) {}
function f(p,) {}

(p) => {};
(p,) => {};
```

尾后逗号也可用于类或对象的方法定义。

```js
class C {
  one(a,) {}
  two(a, b,) {}
}

const obj = {
  one(a,) {},
  two(a, b,) {},
};
```

### 函数调用

下面的两个函数调用都是合法的，并且互相等价。

```js
f(p);
f(p,);

Math.max(10, 20);
Math.max(10, 20,);
```

### 不合法的尾后逗号

仅仅包含逗号的函数参数定义或者函数调用会抛出 `SyntaxError`。而且，当使用剩余参数的时候，并不支持尾后逗号：

```js
function f(,) {} // SyntaxError: missing formal parameter
(,) => {};       // SyntaxError: expected expression, got ','
f(,)             // SyntaxError: expected expression, got ','

function f(...p,) {} // SyntaxError: parameter after rest parameter
(...p,) => {}        // SyntaxError: expected closing parenthesis, got ','
```

解构中的尾后逗号
--------

在使用解构赋值时，尾后逗号也可以用于左侧：

```js
// 带有尾后逗号的数组解构
[a, b,] = [1, 2];

// 带有尾后逗号的对象解构
const o = {
  p: 42,
  q: true,
};
const { p, q, } = o;
```

同样地，在使用剩余参数时，会抛出 `SyntaxError`：

```js
const [a, ...b,] = [1, 2, 3];
// SyntaxError: rest element may not have a trailing comma
```

JSON 中的尾后逗号
-----------

对象中的尾后逗号仅仅在 ECMAScript 5 中引入。由于 JSON 基于 ES5 之前的语法， **JSON 中并不允许尾后逗号**。

下面两行都会抛出 `SyntaxError`：

```js
JSON.parse("[1, 2, 3, 4, ]");
JSON.parse('{"foo" : 1, }');
// SyntaxError JSON.parse: unexpected character
// at line 1 column 14 of the JSON data
```

去掉尾后逗号就行了：

```js
JSON.parse("[1, 2, 3, 4 ]");
JSON.parse('{"foo" : 1 }');
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # prod-Elision |
| ECMAScript Language Specification # prod-ObjectLiteral |
| ECMAScript Language Specification # prod-ArrayLiteral |
| ECMAScript Language Specification # prod-Arguments |
| ECMAScript Language Specification # prod-FormalParameters |
| ECMAScript Language Specification # prod-CoverParenthesizedExpressionAndArrowParameterList |
| ECMAScript Language Specification # prod-NamedImports |
| ECMAScript Language Specification # prod-NamedExports |
| ECMAScript Language Specification # prod-QuantifierPrefix |
| ECMAScript Language Specification # prod-annexB-InvalidBracedQuantifier |

浏览器兼容性
------

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

参见
--

* ECMAScript 初始提案：函数的尾后逗号（由 Jeff Morrison 提出）


语句和声明 - JavaScript

语句和声明
=====

JavaScript 应用程序是由许多语法正确的语句组成的。单个语句可以跨多行。如果每个语句用分号隔开，那么多个语句可以在一行中出现。本页的内容并不是一个关键字，而是一组关键字。

语句和声明（按类别分类）
------------

若需要按字母顺序排列的列表，请参阅左侧边栏。

### 控制流程

`Block`

一个块语句可以用来管理零个或多个语句。该区块是由一对大括号分隔。

`break`

终止当前的循环，switch 或 label 语句，使程序跳到下一个语句执行。

`continue`

终止执行当前或标签循环的语句，直接执行下一个迭代循环。

`Empty`

空语句用来表示没有语句的情况，尽管 JavaScript 语法期望有语句提供。

`if...else`

如果指定的条件是 true，则执行相匹配的一个语句，若为 false，则执行另一个语句。

`switch`

计算表达式，将子句于表达式的值做匹配，执行与该值相关联的语句。

`throw`

抛出一个用户定义的异常。

`try...catch`

标记一个语句块，并指定一个应该抛出异常的反馈。（Marks a block of statements to try, and specifies a response, should an exception be thrown.）

### 声明

`var`

声明一个变量，可同时将其初始化为一个值。

`let`

声明一个块级本地变量，可同时将其初始化为一个值。

`const`

声明一个只读的命名常量。

### 函数和类

`function`

声明一个指定参数的函数。

`function*`

生成器函数使迭代器更容易使用。

`async function`

使用指定的参数声明一个异步函数。

`return`

指定函数的返回值。

`class`

声明一个类。

### 迭代器

`do...while`

创建一个循环来执行语句，直到该语句条件表达式的值为 false。先执行语句，再执行条件表达式，该语句至少会执行一次。

`for`

创建一个由 3 个可选的表达式组成的循环，该循环用括号包裹，分号分割，并在循环体中执行语句。

`for...in`

无序遍历对象的可枚举属性。语句针对每个唯一的属性。

`for...of`

遍历可迭代的对象（包括 `数组`、类数组对象、迭代器和生成器)，对每个不同属性的属性，调用一个自定义的有执行语句的迭代钩子。

`for await...of`

在异步可迭代对象、类数组对象、迭代器和生成器上迭代，调用自定义迭代钩子，其中包含要为每个不同属性的值执行的语句。

`while`

创建一个循环语句，循环会一直持续到该语句条件表达式的值为 false。先执行条件表达式，然后执行语句。

### 其他

`debugger`

调用可用的调试功能。如果没有调试功能可用，该语句不生效。

`export`

用来导出函数，以便这些函数能够被导入到外部模块或其他脚本中。

`import`

用来引入外部的模块或另一个 script 中导出的函数。

`import.meta`

向 JavaScript 模块公开上下文特定的元数据的元属性。

`label`

带标识的语句，与 `break` 或 `continue` 语句一起使用。

`with`
已弃用

拓展一个语句的作用域。

浏览器兼容性
------

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

参见
--

* 表达式和操作符


废弃和过时的特性 - JavaScript

废弃和过时的特性
========

本附录列出了那些已经废弃（仍然可用，但是已计划删除）或者过时（已被删除，无法使用）的 JavaScript 特性。

废弃的特性
-----

这些废弃的特性仍然可以使用，但是使用时一定要保持谨慎，因为它们很可能会在未来的某个时间点被移除。应当将其从需要使用的代码中移除。

ECMAScript 规范的 Annex B 部分列出了其中的一些废弃功能。这一部分被描述为规范性的可选功能，也就是说，web 浏览器主机必须实现这些功能，而非 web 主机可以不实现。这些功能很可能是稳定的，因为删除它们会导致向下兼容的问题，并破坏传统的网站（JavaScript 的设计目标是“不要破坏 web”）。尽管如此，它们还是不能跨平台移植，而且可能不被所有的分析工具所支持，所以建议你不要使用它们，正如 Annex B 的介绍所说：

>
> … All of the language features and behaviors specified in this annex have one or more undesirable characteristics and in the absence of legacy usage would be removed from this specification.（本附录中规定的所有语言特性和行为都有一个或多个不理想的特性，在没有遗留使用的情况下，将从本规范中删除。） …
>
>
> … Programmers should not use or assume the existence of these features and behaviors when writing new ECMAScript code.（程序员在编写新的 ECMAScript 代码时，不应该使用或假设这些特性和行为的存在。） …
>
>
>

其他一些，尽管在规范主体中，也被标记为规范性可选，不应该依赖它们。

### HTML 注释

如果 JavaScript 源码被作为脚本代码解析，它允许 HTML 注释，就像这些脚本是 `<script>` 的标签一部分那样。

以下是在 web 浏览器（或使用支持 Chrome 的 V8 引擎的 Node.js）中运行的有效 JavaScript 代码：

```js
<!-- 注释
console.log("a"); <!-- 另外一段注释
console.log("b");
--> 更多注释
// 输出 "a" 和 "b"
```

`<!--` 和 `-->` 的作用都与 `//` 类似，即起始行注释。`-->` 只在行首有效（以避免与紧跟大于运算符的后缀式自减相混淆），而 `<!--` 可以出现在行的任何地方。

### RegExp

下面的这些属性已经被废弃。这并不影响它们在替换字符串中的使用。

`$1–$9`

括号内的子串匹配结果。

`input, $_`

用于匹配正则表达式的字符串。

`lastMatch, $&`

最后匹配的子串。

`lastParen, $+`

最后一个括号内的子串匹配。

`leftContext, $``

最近一次匹配前的子串。

`rightContext, $'`

最近一次匹配后的子串。

**警告：** 避免使用这些静态属性，因为它们可能导致与外部代码交互时的问题！

`compile()` 方法已被废弃，请构造一个新的 `RegExp` 实例。

### Function

* 函数的 `caller` 和 `arguments.callee` 属性在严格模式中废弃且不可用。
* 应该在函数闭包中使用 `arguments` 对象，而不是将 `arguments` 作为函数的一个属性来访问。

`caller` 和 `arguments` 属性已经废弃，因为它们会泄漏调用函数的对象。应当在函数内部使用 `arguments` 对象来代替函数的 `arguments` 属性。

### Object

* `Object.prototype.__proto__` 访问器已被废弃。使用 `Object.getPrototypeOf` 和 `Object.setPrototypeOf` 代替。这不适用于对象字面量中的 `__proto__` 字面键。
* `Object.prototype.__defineGetter__`、`Object.prototype.__defineSetter__`、`Object.prototype.__lookupGetter__` 和 `Object.prototype.__lookupSetter__` 方法已被废弃。使用 `Object.getOwnPropertyDescriptor` 和 `Object.defineProperty`代替。
### String

* HTML 包装方法，如 `String.prototype.fontsize` 和 `String.prototype.big`。
* `String.prototype.substr` 可能不会很快被删除，但它被定义在 Annex B 中，因此是规范性的可选项。
* `String.prototype.trimLeft` 和 `String.prototype.trimRight` 应替换为 `String.prototype.trimStart` 和 `String.prototype.trimEnd`。
### Date

* `getYear()` 和 `setYear()`方法受千年虫问题影响，已并入 `getFullYear` 和 `setFullYear` 中。
* `toGMTString()` 方法已被弃用，请使用 `toUTCString()` 方法。
### 转义序列

* 在字符串和正则表达式字面中，八进制转义序列（\ 后跟一个、两个或三个八进制数字）被弃用。
* `escape` 和 `unescape` 函数已被弃用。使用 `encodeURI`、`encodeURIComponent`、`decodeURI` 或 `decodeURIComponent` 来对特殊字符的转义序列进行编码和解码。
### 语句

`with` 语句在严格模式中已被弃用且不可用。

在严格模式下，`for...in` 循环头的 `var` 声明中的初始化器被弃用并产生语法错误。在非严格模式下，它们会被静默地忽略。

过时的特性
-----

这些过时的功能已经从 JavaScript 中完全删除，从指定的 JavaScript 版本开始就不能再使用了。

### RegExp

以下是 `RegExp` 实例的属性，不再是 `RegExp` 构造函数的属性：

| 属性 | 描述 |
| --- | --- |
| `global` | 是否针对字符串中所有可能的匹配进行正则表达式测试，或者只针对第一个匹配进行测试。 |
| `ignoreCase` | 在尝试匹配一个字符串时是否忽略大小写。 |
| `lastIndex` | 开始下一次匹配的索引。 |
| `multiline`（也可通过 `RegExp.$*`） | 是否在多行的字符串中搜索。 |
| `source` | 模式的文本。 |

`valueOf()` 方法不再专门用于 `RegExp`。它使用 `Object.prototype.valueOf()`，并返回它本身。

### Function

* Function 的 `arity` 属性已经过时，请使用 `length` 代替。
### Object

| 属性 | 描述 | 替代方法 |
| --- | --- | --- |
| `__count__` | 返回直接在用户定义的对象上的可枚举属性的数量。 | `Object.keys()` |
| `__parent__` | 指向对象的上下文 | 没有直接的替换方法 |
| `__iterator__` | 和遗留的生成器和迭代器一起使用。 | `Symbol.iterator` 和新的迭代协议 |
| `__noSuchMethod__` | 当一个不存在的属性被作为方法调用时，这个方法被调用。 | `Proxy` |
| `Object.prototype.eval()` | 在指定对象的上下文中评估 JavaScript 代码字符串。 | 没有直接的替换方法 |
| `Object.observe()` | 异步地观察一个对象的变化。 | `Proxy` |
| `Object.unobserve()` | 移除观察器。 | `Proxy` |
| `Object.getNotifier()` | 创建一个通知者对象，允许用 `Object.observe()` 来综合触发一个变化的观察者。 | 没有直接的替换方法 |
| `Object.prototype.watch()` | 在一个属性上附加一个处理器回调，当属性被分配时被调用。 | `Proxy` |
| `Object.prototype.unwatch()` | 移除一个属性上的观察处理器。 | `Proxy` |

### String

* 非标准的字符串通用方法，如 `String.slice(myStr, 0, 12)`、`String.replace(myStr, /\./g, "!")` 等，在 Firefox 1.5（JavaScript 1.6）中引入，在 Firefox 53 中被废弃，并在 Firefox 68 中删除。你可以使用 `String.prototype` 和 `Function.call` 来代替。
* `String.prototype.quote` 已在 Firefox 37 中移除。
* `String.prototype.search`、`String.prototype.match` 和 `String.prototype.replace` 中非标准的 `flags` 参数已被淘汰。
### WeakMap

* `WeakMap.prototype.clear()` 在 Firefox 20 中添加，在 Firefox 46 中移除。不可能遍历一个 `WeakMap` 中的所有键。
### Date

* `Date.prototype.toLocaleFormat()` 使用与 C 语言中 `strftime()` 函数所期望的格式相同的格式字符串，已经过时了。使用 `toLocaleString()` 或 `Intl.DateTimeFormat` 代替。
### Array

非标准的 Array 通用方法，如 `Array.slice(myArr, 0, 12)`、`Array.forEach(myArr, myFn)` 等，在 Firefox 1.5（JavaScript 1.6）中引入，在 Firefox 68 中被废弃，并在 Firefox 71 中删除。你可以使用 `Array.prototype` 和 `Function.call` 来代替。

| 属性 | 描述 | 替代 |
| --- | --- | --- |
| `Array.observe()` | 异步地观察数组的变化 | `Proxy` |
| `Array.unobserve()` | 移除观察器。 | `Proxy` |

### Number

* `Number.toInteger()` 已经过时了。使用 `Math.floor`、`Math.round` 或其他方法代替。
### Proxy

* `Proxy.create` 和 `Proxy.createFunction` 已经过时了，请使用 `Proxy()` 构造函数。
* 下列陷入已经过时：
    + `hasOwn`（bug 980565，Firefox 33）
    + `getEnumerablePropertyKeys`（bug 783829，Firefox 37）
    + `getOwnPropertyNames`（bug 1007334，Firefox 33）
    + `keys`（bug 1007334，Firefox 33）
### ParallelArray

* `ParallelArray` 已过时。
### 语句

* `for each...in` 已经过时了，使用 `for...of` 代替。
* let 块和 let 表达式已经过时了。
* 表达式闭包（`function () 1` 作为`function () { return 1; }` 的缩写）已经过时。使用常规的 `function` 或箭头函数代替。
### 获取源文本

数组、数字、字符串等的 `toSource()` 方法和 `uneval()` 全局函数已经过时。使用 `toString()`，或者编写你自己的序列化方法来代替。

### 遗留的生成器和迭代器

遗留的生成器函数语句和遗留的生成器函数表达式被移除。传统的生成器函数语法重用了 `function` 关键字，当主体中有一个或多个 `yield` 表达式时，它自动成为生成器函数——现在这是一个语法错误。使用 `function*` 语句和 `function*` 表达式代替。

数组推导式和生成器推导式已被移除。
```

// 遗留的数组推导式
[for (x of iterable) x]
[for (x of iterable) if (condition) x]
[for (x of iterable) for (y of iterable) x + y]

// 遗留的生成器推导式
(for (x of iterable) x)
(for (x of iterable) if (condition) x)
(for (x of iterable) for (y of iterable) x + y)
```

Firefox 26 版本之前实现了另一个迭代器协议，与标准的迭代器协议类似。当一个对象实现了 `next()` 方法时，它就是一个遗留的迭代器，该方法在每次调用时产生一个值，并在迭代结束时抛出一个 `StopIteration` 对象。这种遗留迭代器协议与标准迭代器协议不同：

* 值直接作为调用 `next()` 的返回值，而不是 `IteratorResult` 对象的 `value` 属性。
* 迭代终止是通过抛出一个 `StopIteration` 对象，而不是通过 `IteratorResult` 对象的 `done` 属性来表达。

这个功能，连同 `StopIteration` 全局构造函数，在 Firefox 58+ 中被移除。未来可以使用 `for...of` 循环和迭代器协议。

### 变量引用语法（Sharp variable）

变量引用语法已经过时。要创建循环结构，请使用临时变量代替。


表达式和运算符 - JavaScript

表达式和运算符
=======

该章节说明了 JavaScript 语言所有的运算符，表达式和关键字。

表达式和运算符分类
---------

要查看按字母顺序排列的列表，请参阅左边的侧边栏。

### 主要表达式

JavaScript 中的基本关键字和常用表达式。

`this`

`this` 关键字指向函数的执行上下文。

`function`

`function` 关键字定义了函数表达式。

`class`

`class` 关键字定义了类表达式。

`function*`

`function*` 关键字定义了一个 generator 函数表达式。

`yield`

暂停和恢复 generator 函数。

`yield*`

委派给另外一个 generator 函数或可迭代的对象。

`async function`

`async function` 定义一个异步函数表达式。

`await`

暂停或恢复执行异步函数，并等待 promise 的 resolve/reject 回调。

`async function*`

`async function*` 定义了一个异步生成器函数表达式。

`[]`

数组初始化/字面量语法。

`{}`

对象初始化/字面量语法。

`/ab+c/i`

正则表达式字面量语法。

`( )`

分组操作符。

### 左表达式

左边的值是赋值的目标。

属性访问符

成员运算符提供了对对象的属性或方法的访问（`object.property` 和 `object["property"]`）。

`new`

`new` 运算符创建了构造函数实例。

`new.target`

在构造器中，`new.target` 指向 `new` 调用的构造器。

`import.meta`

向 JavaScript 模块暴露特定上下文的元数据属性的对象。

`super`

`super` 关键字调用父类的构造器。

`...obj`

展开运算符允许在需要多个参数（用于函数调用）或多个元素（用于数组字面量）的地方展开表达式。

### 自增和自减

前置/后置自增运算符和前置/后置自减运算符。

`A++`

后置自增运算符。

`A--`

后置自减运算符。

`++A`

前置自增运算符。

`--A`

前置自减运算符。

### 一元运算符

一元运算符只有一个操作数。

`delete`

`delete` 运算符用来删除对象的属性。

`void`

`void` 运算符表示表达式放弃返回值。

`typeof`

`typeof` 运算符用来判断给定对象的类型。

`+`

一元加运算符将操作转换为 Number 类型。

`-`

一元减运算符将操作转换为 Number 类型并取反。

`~`

按位非运算符。

`!`

逻辑非运算符。

### 算术运算符

算术运算符以二个数值（字面量或变量）作为操作数，并返回单个数值。

`+`（加）

加法运算符。

`-`

减法运算符。

`/`

除法运算符。

`*`

乘法运算符。

`%`

取模运算符。

`**`

求幂运算符。

### 关系运算符

比较运算符比较两个操作数并返回基于比较结果的布尔值。

`in`

`in` 运算符用来判断对象是否拥有给定属性。

`instanceof`

`instanceof` 运算符判断一个对象是否是另一个对象的实例。

`<`（小于）

小于运算符。

`>`（大于）

大于运算符。

`<=`

小于等于运算符。

`>=`

大于等于运算符。

**备注：** `=>` 不是运算符，而是`箭头函数`的表示符。

### 相等运算符

如果相等，操作符返回的是布尔类型的 true，否则是 false。

`==`

相等运算符。

`!=`

不等运算符。

`===`

全等运算符。

`!==`

非全等运算符。

### 位移运算符

在二进制的基础上对数字进行移动操作

`<<`

按位左移运算符。

`>>`

按位右移运算符。

`>>>`

按位无符号右移运算符。

### 二进制位运算符

二进制运算符将它们的操作数作为 32 个二进制位（0 或 1）的集合，并返回标准的 JavaScript 数值。

`&`

按位与（AND）。

`|`

按位或（OR）。

`^`

位异或（XOR）。

### 二元逻辑运算符

逻辑运算符典型的用法是用于布尔（逻辑）值运算，它们返回布尔值。

`&&`

逻辑与。

`||`

逻辑或。

`??`

空值合并运算符，如果 ?? 前面是 null 或 undefined，取后面的默认值。

### 可选链运算符

* `?.`
    + 如果引用是空值（`null` 或 `undefined`），可选链运算符将返回 `undefined` 而不是导致错误。
### 条件（三元）运算符

`(condition ? ifTrue : ifFalse)`

条件元素运算符把两个结果中其中一个符合运算逻辑的值返回。

### 赋值运算符

赋值运算符将右边的操作数的值分配给左边的操作数。

`=`

赋值运算符。

`*=`

赋值乘积。

`**=`

求幂赋值。

`/=`

赋值商。

`%=`

赋值求余。

`+=`

赋值求和。

`-=`

赋值求差。

`<<=`

左位移。

`>>=`

右位移。

`>>>=`

无符号右位移。

`&=`

赋值与。

`^=`

赋值按位异或。

`|=`

赋值或。

`&&=`

逻辑和赋值运算符。

`||=`

逻辑或赋值运算符。

`??=`

逻辑空赋值运算符。

`[a, b] = arr`, `{ a, b } = obj`

解构赋值允许你使用类似于数组或对象字面量的语法将数组或对象的属性赋值给变量。

### 逗号运算符

逗号操作符 `,`

逗号操作符允许在一个判断状态中有多个表达式去进行运算并且最后返回最后一个表达式的值。

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-addition-operator-plus |
| ECMAScript Language Specification # sec-assignment-operators |
| ECMAScript Language Specification # sec-async-function-definitions |
| ECMAScript Language Specification # sec-async-generator-function-definitions |
| ECMAScript Language Specification # prod-BitwiseANDExpression |
| ECMAScript Language Specification # sec-bitwise-not-operator |
| ECMAScript Language Specification # prod-BitwiseORExpression |
| ECMAScript Language Specification # prod-BitwiseXORExpression |
| ECMAScript Language Specification # sec-class-definitions |
| ECMAScript Language Specification # sec-comma-operator |
| ECMAScript Language Specification # sec-conditional-operator |
| ECMAScript Language Specification # sec-postfix-decrement-operator |
| ECMAScript Language Specification # sec-delete-operator |
| ECMAScript Language Specification # sec-destructuring-assignment |
| ECMAScript Language Specification # sec-destructuring-binding-patterns |
| ECMAScript Language Specification # sec-multiplicative-operators |
| ECMAScript Language Specification # sec-equality-operators |
| ECMAScript Language Specification # sec-exp-operator |
| ECMAScript Language Specification # sec-function-definitions |
| ECMAScript Language Specification # sec-generator-function-definitions |
| ECMAScript Language Specification # sec-relational-operators |
| ECMAScript Language Specification # sec-grouping-operator |
| ECMAScript Language Specification # sec-import-calls |
| ECMAScript Language Specification # prod-ImportMeta |
| HTML Standard # hostgetimportmetaproperties |
| ECMAScript Language Specification # sec-postfix-increment-operator |
| ECMAScript Language Specification # sec-left-shift-operator |
| ECMAScript Language Specification # prod-LogicalANDExpression |
| ECMAScript Language Specification # sec-logical-not-operator |
| ECMAScript Language Specification # prod-LogicalORExpression |
| ECMAScript Language Specification # sec-new-operator |
| ECMAScript Language Specification # sec-built-in-function-objects |
| ECMAScript Language Specification # sec-null-value |
| ECMAScript Language Specification # prod-CoalesceExpression |
| ECMAScript Language Specification # sec-object-initializer |
| ECMAScript Language Specification # prod-OptionalExpression |
| ECMAScript Language Specification # sec-property-accessors |
| ECMAScript Language Specification # sec-signed-right-shift-operator |
| ECMAScript Language Specification # prod-SpreadElement |
| ECMAScript Language Specification # prod-ArgumentList |
| ECMAScript Language Specification # prod-PropertyDefinition |
| ECMAScript Language Specification # sec-subtraction-operator-minus |
| ECMAScript Language Specification # sec-super-keyword |
| ECMAScript Language Specification # sec-this-keyword |
| ECMAScript Language Specification # sec-typeof-operator |
| ECMAScript Language Specification # sec-unary-minus-operator |
| ECMAScript Language Specification # sec-unary-plus-operator |
| ECMAScript Language Specification # sec-unsigned-right-shift-operator |
| ECMAScript Language Specification # sec-void-operator |
| ECMAScript Language Specification # prod-YieldExpression |
| ECMAScript Language Specification # sec-generator-function-definitions-runtime-semantics-evaluation |

浏览器兼容性
------

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

参见
--

* 运算符优先级


JavaScript 错误参考 - JavaScript

JavaScript 错误参考
===============

下面列出了 JavaScript 抛出的错误。这些错误是有用的调试帮助，但报告的问题并不总是十分明了。下面的页面提供有关这些错误的详细信息。每个错误都是基于 `Error` 的对象，并且具有名称和消息。

Web 控制台中显示的错误可能包含指向下面相应页面的链接，以帮助你快速理解代码中的问题。

错误列表
----

在此列表中，每个页面按名称（错误类型）和信息（更详细的容易理解的错误信息）列出。总之，这两者提供了理解和解决错误的线索。有关更多信息，请按照以下链接！

* Error: Permission denied to access property "x"
* InternalError: too much recursion
* RangeError: argument is not a valid code point
* RangeError: invalid array length
* RangeError: invalid date
* RangeError: precision is out of range
* RangeError: radix must be an integer
* RangeError: repeat count must be less than infinity
* RangeError: repeat count must be non-negative
* ReferenceError: "x" is not defined
* ReferenceError: assignment to undeclared variable "x"
* ReferenceError: can't access lexical declaration`X' before initialization
* ReferenceError: deprecated caller or arguments usage
* ReferenceError: invalid assignment left-hand side
* ReferenceError: reference to undefined property "x"
* SyntaxError: "0"-prefixed octal literals and octal escape seq. are deprecated
* SyntaxError: "use strict" not allowed in function with non-simple parameters
* SyntaxError: "x" is a reserved identifier
* SyntaxError: JSON.parse: bad parsing
* SyntaxError: Unexpected token
* SyntaxError: Using //@ to indicate sourceURL pragmas is deprecated. Use //# instead
* SyntaxError: a declaration in the head of a for-of loop can't have an initializer
* SyntaxError: applying the 'delete' operator to an unqualified name is deprecated
* SyntaxError: for-in loop head declarations may not have initializers
* SyntaxError: function statement requires a name
* SyntaxError: identifier starts immediately after numeric literal
* SyntaxError: illegal character
* SyntaxError: invalid regular expression flag "x"
* SyntaxError: missing ) after argument list
* SyntaxError: missing ) after condition
* SyntaxError: missing : after property id
* SyntaxError: missing ; before statement
* SyntaxError: missing = in const declaration
* SyntaxError: missing ] after element list
* SyntaxError: missing formal parameter
* SyntaxError: missing name after . operator
* SyntaxError: missing variable name
* SyntaxError: missing } after function body
* SyntaxError: missing } after property list
* SyntaxError: redeclaration of formal parameter "x"
* SyntaxError: return not in function
* SyntaxError: test for equality (==) mistyped as assignment (=)?
* SyntaxError: unterminated string literal
* TypeError: "x" has no properties
* TypeError: "x" is (not) "y"
* TypeError: "x" is not a constructor
* TypeError: "x" is not a function
* TypeError: "x" is not a non-null object
* TypeError: "x" is read-only
* TypeError: 'x' is not iterable
* TypeError: More arguments needed
* TypeError: Reduce of empty array with no initial value
* TypeError: can't assign to property "x" on "y": not an object
* TypeError: can't define property "x": "obj" is not extensible
* TypeError: can't delete non-configurable array element
* TypeError: can't redefine non-configurable property "x"
* TypeError: cyclic object value
* TypeError: invalid 'in' operand "x"
* TypeError: invalid 'instanceof' operand 'x'
* TypeError: invalid Array.prototype.sort argument
* TypeError: invalid assignment to const "x"
* TypeError: property "x" is non-configurable and can't be deleted
* TypeError: setting getter-only property "x"
* URIError: malformed URI sequence
* Warning: -file- is being assigned a //# sourceMappingURL, but already has one
* Warning: unreachable code after return statement
* X.prototype.y called on incompatible type


词法文法 - JavaScript

词法文法
====

这部分描述了 JavaScript 的词法（lexical grammar）。ECMAScript 源码文本会被从左到右扫描，并被转换为一系列的输入元素，包括 token、控制符、行终止符、注释和空白符。ECMAScript 定义了一些关键字、字面量以及行尾分号补全的规则。

格式控制符
-----

格式控制符用于控制对源码文本的解释，但是并不会显示出来。

| 代码点 | 名称 | 缩写 | 说明 |
| --- | --- | --- | --- |
| `U+200C` | 零宽度非结合子 | <ZWNJ> | 放置在一些经常会被当成连字的字符之间，用于将它们分别以独立形式显示（Wikipedia） |
| `U+200D` | 零宽度结合子 | <ZWJ> | 放置在一些通常不会被标记为连字的字符之间，用于将这些字符以连字形式显示（Wikipedia） |
| `U+FEFF` | 字节流方向标识 | <BOM> | 在脚本开头使用，除了将脚本标记为 Unicode 格式以外，还用来标记文本的字节流方向（Wikipedia） |

空白符
---

空白符提升了源码的可读性，并将标记 (tokens) 区分开。这些符号通常不影响源码的功能。通常可以用压缩器来移除源码中的空白，减少数据传输量。

| 代码点 | 名称 | 缩写 | 说明 | 转义序列 |
| --- | --- | --- | --- | --- |
| U+0009 | 制表符 | <HT> | 水平制表符 | \t |
| U+000B | 垂直制表符 | <VT> | 垂直制表符 | \v |
| U+000C | 分页符 | <FF> | 分页符（Wikipedia） | \f |
| U+0020 | 空格 | <SP> | 空格 |  |
| U+00A0 | 无间断空格 | <NBSP> | 在该空格处不会换行 |  |
| Others | 其他 Unicode 空白 | <USP> | Wikipedia 上对 Unicode 空白的介绍 |  |

行终止符
----

除了空白符之外，行终止符也可以提高源码的可读性。不同的是，行终止符可以影响 JavaScript 代码的执行。行终止符也会影响自动分号补全的执行。在正则表达式中，行终止符会被 **\s** 匹配。

在 ECMAScript 中，只有下列 Unicode 字符会被当成行终止符，其他的行终止符（比如 Next Line、NEL、U+0085 等）都会被当成空白符。

| 编码 | 名称 | 缩写 | 说明 | 转义序列 |
| --- | --- | --- | --- | --- |
| U+000A | 换行符 | <LF> | 在 UNIX 系统中起新行 | \n |
| U+000D | 回车符 | <CR> | 在 Commodore 和早期的 Mac 系统中起新行 | \r |
| U+2028 | 行分隔符 | <LS> | Wikipedia |  |
| U+2029 | 段分隔符 | <PS> | Wikipedia |  |

注释
--

注释用来在源码中增加提示、笔记、建议、警告等信息，可以帮助阅读和理解源码。在调试时，可以用来将一段代码屏蔽掉，防止其运行；这也是一个有价值的调试工具。

在 JavaScript 中，有两种常见的添加注释的方法：行注释和块注释。另外，也有一种特殊的 hashbang 注释语法。

### 行注释

第一种是 `//` 风格的注释；会将该行中符号以后的文本都视为注释：

```js
function comment() {
  // 这是单行注释
  console.log("Hello world!");
}
comment();
```

### 块注释

第二种是 `/* */` 风格的注释，这种方式更加灵活：

比如，可以在单行内使用多行注释：

```js
function comment() {
  /\* 这是单行注释 \*/
  console.log("Hello world!");
}
comment();
```

也可以用来实现多行注释：

```js
function comment() {
  /\* 这是多行注释，
 注意在写完注释前无需终止注释 \*/
  console.log("Hello world!");
}
comment();
```

多行注释也可以用于行内注释，但这样会使代码可读性变差，所以要谨慎使用：

```js
function comment(x) {
  console.log("Hello " + x /\* 引入 x 的值 \*/ + " !");
}
comment("world");
```

另外，块注释也可以用来屏蔽一段代码，只要将这段代码用块注释包裹起来就可以了：

```js
function comment() {
  /\* console.log("Hello world!"); \*/
}
comment();
```

这种情况下，注释中的 `console.log()` 的调用始终无效。这种方式可以屏蔽任意多行的代码，也可以屏蔽一行代码的一部分。

包含至少一个行结束符的块状注释的行为与自动分号插入中的行结束符相似。

### Hashbang 注释

**Hashbang 注释**是一种特殊的注释语法，其行为与单行注释（`//`）完全一样，只是它以 `#!` 开头，并且**只在脚本或模块的最开始处有效**。注意，`#!` 标志之前不能有任何空白字符。注释由 `#!` 之后的所有字符组成直到第一行的末尾；只允许有一条这样的注释。JavaScript 中的 hashbang 注释类似于 Unix 中的 shebang，它提供了一个特定的 JavaScript 解释器的路径，你想用它来执行这个脚本。在 hashbang 注释标准化之前，它已经在非浏览器主机（如 Node.js）中得到了事实上的实现，在那里，它在被传递给引擎之前被从源文本中剥离。示例如下：

```js
#!/usr/bin/env node

console.log("Hello world");
```

JavaScript 的解释器会把它视为普通注释——只有当脚本直接在 shell 中运行时，它对 shell 才有语义意义。

**警告：** 如果你想让脚本直接在 shell 环境中运行，请用不含 BOM 的 UTF-8 编码。虽然 BOM 不会对在浏览器中运行的代码造成任何问题——在 UTF-8 解码过程中，分析源文本之前，BOM 就已经被剥离了——但如果前面有一个 BOM 字符，Unix/Linux shell 就不会识别该注释。

你只能使用 `#!` 注释样式以指定 JavaScript 解释器。在所有其他情况下，只需使用 `//` 注释（或多行注释）。

关键字
---

### ECMAScript 6 中的保留关键字

* `break`
* `case`
* `catch`
* `class`
* `const`
* `continue`
* `debugger`
* `default` (en-US)
* `delete`
* `do`
* `else`
* `export`
* `extends`
* `finally`
* `for`
* `function`
* `if`
* `import`
* `in`
* `instanceof`
* `new`
* `return`
* `super`
* `switch`
* `this`
* `throw`
* `try`
* `typeof`
* `var`
* `void`
* `while`
* `with`
* `yield`
### 未来保留关键字

在 ECMAScript 规格中，这些关键字被当成关键字保留。目前它们没有特殊功能，但是在未来某个时间可能会加上。所以这些关键字不能当成标识符使用。这些关键字在严格模式和非严格模式中均不能使用。

* `enum`

以下关键字只在严格模式中被当成保留关键字：

* `implements`
* `interface`
* `let`
* `package`
* `private`
* `protected`
* `public`
* `static`

以下关键字只在模块代码中被当成保留关键字：

* `await`

#### 之前标准中的保留关键字

这里是之前版本中的 ECMAScript（1 到 3）中的保留关键字：

* `abstract`
* `boolean`
* `byte`
* `char`
* `double`
* `final`
* `float`
* `goto`
* `int`
* `long`
* `native`
* `short`
* `synchronized`
* `transient`
* `volatile`

另外，字面量 `null`、`true` 和 `false` 同样不能被当成标识使用。

### 保留字的使用

事实上保留字是仅针对标识符（Identifier）的文法定义而言的（而不是标识符名（IdentifierName）的文法定义）。如 es5.github.com/#A.1 中所描述的，这些都是不排斥保留字的标识符名。

```js
a.import
a["import"]
a = { import: "test" }.
```

另一方面，如下用法是不允许的。因为它是一个标识符，而标识符的文法定义是除保留字以外的标识符名。标识符用于函数声明式和函数表达式。

```js
function import() {} // Illegal.
```

字面量
---

### 空字面量

更多信息可以参考 `null`。

```js
null
```

### 布尔字面量

更多信息可以参考`Boolean`

```js
true
false
```

### 数值字面量

#### 十进制

```js
1234567890
42
```

请注意，十进制数值字面量可以以 0 开头，但是如果 0 以后的最高位比 8 小，数值将会被认为是八进制而不会报错。更多信息可以参考 Firefox bug 957513 和 `parseInt()`。

#### 二进制

二进制表示为开头是 0 后接大写或小写的 B（`0b`或者`0B`）。这是 ECMAScript 6 中的新语法，可以参考下面的浏览器兼容性表格。如果`0b`之后有除了 0 或 1 以外的数字，将会抛出`SyntaxError`：“Missing binary digits after 0b”。

```js
0b10000000000000000000000000000000 // 2147483648
0b01111111100000000000000000000000 // 2139095040
0B00000000011111111111111111111111 // 8388607
```

#### 八进制

八进制表示为开头是 0 后接大写或小写的 O（`0o`或`0O`）。这是 ECMAScript 6 中的新语法，可以参考下面的浏览器兼容性表格。如果有不在（01234567）中的数字，将会抛出`SyntaxError`：“Missing octal digits after 0o”。

```js
0O755 // 493
0o644 // 420
```

#### 十六进制

十六进制表示为开头是 0 后接大写或小写的 X（`0x`或`0X`）。如果有不在（0123456789ABCDEF）中的数字，将会抛出`SyntaxError`：“Identifier starts immediately after numeric literal”。

```js
0xFFFFFFFFFFFFFFFFF // 295147905179352830000
0x123456789ABCDEF   // 81985529216486900
0XA                 // 10
```

#### 数值分隔符

可以使用下划线（`_`，`U+005F`）作为分隔符以增强数值字面量的可读性：

```js
1\_000\_000\_000\_000
1\_050.95
0b1010\_0001\_1000\_0101
0o2\_2\_5\_6
0xA0\_B0\_C0
1\_000\_000\_000\_000\_000\_000\_000n
```

请注意这些限制：

```js
// 不允许连续出现多个下划线
100__000; // SyntaxError

// 不允许使用下划线作为数值字面量的结尾
100_; // SyntaxError

// 不允许在前导零之后使用下划线
0\_1; // SyntaxError
```

### 对象字面量

更多信息可以参考 `Object` 和对象初始化器。

```js
var o = { a: "foo", b: "bar", c: 42 };

// ES6 中的简略表示方法
var a = "foo",
  b = "bar",
  c = 42;
var o = { a, b, c };
// 不需要这样
var o = { a: a, b: b, c: c };
```

### 数组字面量

更多信息可以参考 `Array`。

```js
[1954, 1974, 1990, 2014];
```

### 字符串字面量

```js
'foo'
"bar"
```

#### 十六进制转义序列

```js
"\xA9"; // "©"
```

#### Unicode 转义序列

Unicode 转义序列要求在`\u`之后至少有四个字符。

```js
"\u00A9"; // "©"
```

#### Unicode 编码转义

ECMAScript 6 新增特性。使用 Unicode 编码转义，任何字符都可以被转义为十六进制编码。最高可以用到`0x10FFFF`。使用单纯的 Unicode 转义通常需要写成分开的两半以达到相同的效果。

可以参考`String.fromCodePoint()`和`String.prototype.codePointAt()`。

```js
"\u{2F804}";

// 使用单纯 Unicode 转义
"\uD87E\uDC04";
```

### 正则表达式字面量

更多信息可以参考 `RegExp`。

```js
/ab+c/g

// 一个空的正则表达式字面量
// 必须有一个空的非捕获分组
// 以避免被当成是行注释符号
/(?:)/
```

### 模板字面量

更多信息可以参考template strings。

```js
`string text`

`string text line 1
 string text line 2`

`string text ${expression} string text`

tag`string text ${expression} string text`
```

自动分号补全
------

一些 JavaScript 语句必须用分号结束，所以会被自动分号补全 (ASI) 影响：

* 空语句
* `let`、`const`、变量声明
* `import`、`export`、模块定义
* 表达式语句
* `debugger`
* `continue`、`break`、`throw`
* `return`

ECMAScript 规格提到自动分号补全的三个规则。

1. 当出现一个不允许的行终止符或“}”时，会在其之前插入一个分号。

```js
{ 1 2 } 3

// 将会被 ASI 转换为

{ 1 2 ;} 3;
```

2. 当捕获到标识符输入流的结尾，并且无法将单个输入流转换为一个完整的程序时，将在结尾插入一个分号。
 在下面这段中，由于在 `b` 和 `++` 之间出现了一个行终止符，所以 `++` 未被当成变量 `b` 的后置运算符 (en-US)。

```js
a = b
++c

// 将被 ASI 转换为

a = b;
++c;
```

3. 当语句中包含语法中的限制产品后跟一个行终止符的时候，将会在结尾插入一个分号。带“这里没有行终止符”规则的语句有：
    * 后置运算符（`++` 和 `--`）
    * `continue`
    * `break`
    * `return`
    * `yield`、`yield*`
    * `module`
```js
return
a + b

// 将被 ASI 转换为

return;
a + b;
```

浏览器兼容性
------

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

参见
--

* Jeff Walden: Binary and octal numbers
* Mathias Bynens: JavaScript character escape sequences
* `Boolean`
* `Number`
* `RegExp`
* `String`


函数 - JavaScript

函数
==

一般来说，一个函数是可以通过外部代码调用的一个“子程序”（或在递归的情况下由内部函数调用）。像程序本身一样，一个函数由称为函数体的一系列语句组成。值可以传递给一个函数，函数将返回一个值。在 JavaScript 中，函数是**头等 (**first-class**)**对象，因为它们可以像任何其他**对象**一样具有属性和方法。它们与其他对象的区别在于函数可以被调用。简而言之，它们是`Function`对象。
有关更多示例和说明，请参阅有关函数的 JavaScript 指南。

描述
--

在 JavaScript 中，每个函数其实都是一个`Function`对象。查看`Function`页面`了解其`属性和方法。

如果一个函数中没有使用 return 语句，则它默认返回`undefined`。要想返回一个特定的值，则函数必须使用 `return` 语句来指定一个要返回的值。(使用new关键字调用一个构造函数除外)。

调用函数时，传递给函数的值被称为函数的实参（值传递），对应位置的函数参数名叫作形参。如果实参是一个包含原始值 (数字，字符串，布尔值) 的变量，则就算函数在内部改变了对应形参的值，返回后，该实参变量的值也不会改变。如果实参是一个对象引用，则对应形参会和该实参指向同一个对象。假如函数在内部改变了对应形参的值，返回后，实参指向的对象的值也会改变：

```js
/\* 定义函数 myFunc \*/
function myFunc(theObject) {
  //实参 mycar 和形参 theObject 指向同一个对象。
  theObject.brand = "Toyota";
}

/\*
 \* 定义变量 mycar;
 \* 创建并初始化一个对象;
 \* 将对象的引用赋值给变量 mycar
 \*/
var mycar = {
  brand: "Honda",
  model: "Accord",
  year: 1998,
};

/\* 弹出 'Honda' \*/
window.alert(mycar.brand);

/\* 将对象引用传给函数 \*/
myFunc(mycar);

/\*
 \* 弹出 'Toyota',对象的属性已被修改。
 \*/
console.log(mycar.brand);
```

在函数执行时，`this` 关键字并不会指向正在运行的函数本身，而是指向调用该函数的对象。所以，如果你想在函数内部获取函数自身的引用，只能使用函数名或者使用arguments.callee属性 (\*\*严格模式\*\*下不可用)，如果该函数是一个匿名函数，则你只能使用后者。

函数定义
----

定义函数有多种方法：

### 函数声明 (函数语句)

有一个特殊的语法来声明函数 (查看函数语句了解详情)：
```

function name([param[, param[, ... param]]]) { statements }
```

`name`

函数名。

`param`

传递给函数的参数的名称。

`statements`

组成函数体的声明语句。

### 函数表达式 (`function` expression)

函数表达式和函数声明非常相似，它们甚至有相同的语法（查看函数表达式了解详情）。一个函数表达式可能是一个更大的表达式的一部分。可以定义函数“名字”（例如可以在调用堆栈时使用）或者使用“匿名”函数。函数表达式不会提升，所以不能在定义之前调用。
```

var myFunction = function name([param[, param[, ... param]]]) { statements }
```

`name`

函数名，可以省略。当省略函数名的时候，该函数就成为了匿名函数。

`param`

传递给函数的参数的名称。

`statements`

组成函数体的声明语句。

下面是**匿名**函数的一个例子（函数没有名字）：

```js
var myFunction = function () {
  // statements
};
```

也可以在定义时为函数**命名**：

```js
var myFunction = function namedFunction() {
  // statements
};
```

具名函数表达式的好处是当我们遇到错误时，堆栈跟踪会显示函数名，容易寻找错误。

可以看到，上面的两个例子都不以 function 开头。不以 function 开头的函数语句就是函数表达式定义。

当函数只使用一次时，通常使用**IIFE (*Immediately Invokable Function Expressions*)。**

```js
(function () {
  statements;
})();
```

**IIFE**是在函数声明后立即调用的函数表达式。

### 函数生成器声明 (`function*` 语句)

函数声明有一种特殊的语法 (详情请查阅`function* statement` )：
```

function* name([param[, param[, ...param]]]) { statements }
```

`name`

函数名称。

`param`

传递给函数的参数的名称。

`statements`

组成函数体的声明语句。

### 函数生成器表达式 (`function*`表达式)

构造函数表达式和函数声明类似，并且有着相同的语法 (详情请查阅 `function* expression` ):
```

function* [name]([param] [, param] [..., param]) { statements }
```

`name`

函数名称。函数名可以被省略，在这种情况下该函数将变成匿名函数。

`param`

传递给函数的参数的名称。

`statements`

组成函数体的声明语句。

### 箭头函数表达式 (=>)

箭头函数表达式有着更短的语法，并在词汇方面结合这个值 (详情请查阅 arrow functions ):
```

([param] [, param]) => { statements } param => expression
```

`param`

参数名称。零参数需要用 () 表示。只有一个参数时不需要括号。(例如 `foo => 1`)

`statements or expression`

多个声明 statements 需要用大括号括起来，而单个表达式时则不需要。表达式 expression 也是该函数的隐式返回值。

### `Function`构造函数

**备注：** 不推荐使用 `Function` 构造函数创建函数，因为它需要的函数体作为字符串可能会阻止一些 JS 引擎优化，也会引起其他问题。

所有其他对象，`Function` 对象可以用 new 操作符创建：
```

new Function (arg1, arg2, ... argN, functionBody)
```

`arg1, arg2, ... argN`

函数使用零个或多个名称作为正式的参数名称。每一个必须是一个符合有效的 JavaScript 标识符规则的字符串或用逗号分隔的字符串列表，例如“x”,“theValue”或“a,b”。

`functionBody`

一个构成的函数定义的，包含 JavaScript 声明语句的字符串。

把 Function 的构造函数当作函数一样调用 (不使用 new 操作符) 的效果与作为 Function 的构造函数调用一样。

### 生成器函数的构造函数

**备注：** `GeneratorFunction` 不是一个全局对象，但可以从构造函数实例取得。(详情请查阅生成器函数).

**备注：** 不推荐使用构造器函数的构造函数 (`GeneratorFunction` constructor) 创建函数，因为它需要的函数体作为字符串可能会阻止一些 JS 引擎优化，也会引起其他问题。

所有其他对象，`GeneratorFunction` 对象可以用 new 操作符创建：
```

new GeneratorFunction (arg1, arg2, ... argN, functionBody)
```

`arg1, arg2, ... argN`

函数使用零个或多个名称作为正式的参数名称。每一个必须是一个符合有效的 JavaScript 标识符规则的字符串或用逗号分隔的字符串列表，例如“x”,“theValue”或“a,b”。

`functionBody`

一个构成的函数定义的，包含 JavaScript 声明语句的字符串。

把 Function 的构造函数当作函数一样调用 (不使用 new 操作符) 的效果与作为 Function 的构造函数调用一样。

函数参数
----

### 默认参数

如果没有值或传入了未定义的值，默认函数参数允许形式参数使用默认值初始化。参见：默认参数。

### 剩余参数

剩余参数语法允许将数量不限的参数描述成一个数组。参见：剩余参数。

`arguments`对象
-------------

你可以参阅在函数里使用`arguments`对象的函数参数。参见：arguments。

* `arguments`: 一个包含了传递给当前执行函数参数的类似于数组的对象。
* `arguments.callee` (en-US)
已弃用
: 当前正在执行的函数。
* `arguments.caller`
已弃用
 : 调用当前执行函数的函数。
* `arguments.length`: 传给函数的参数的数目。
方法函数定义
------

### Getter 和 setter 函数

你可以在支持添加新属性的任何标准的内置对象或用户定义的对象内定义 getter(访问方法) 和 setter(设置方法)。使用对象字面量语法定义 getters 和 setters 方法。

get

当查找某个对象属性时，该对象属性将会与被调用函数绑定。

set

当试图设置该属性时，对象属性与被调用函数绑定。

### 方法定义语法

从 ECMAScript 6 开始，你可以用更短的语法定义自己的方法，类似于 getters 和 setters。详情请查阅 method definitions .

```js
var obj = {
  foo() {},
  bar() {},
};
```

构造函数 vs 函数声明 vs 函数表达式
---------------------

对比下面的例子：

一个用`Function`构造函数定义的函数，被赋值给变量 multiply：

```js
var multiply = new Function("x", "y", "return x \* y");
```

一个名为`multiply`的函数声明：

```js
function multiply(x, y) {
  return x \* y;
} // 没有分号
```

一个匿名函数的函数表达式，被赋值给变量`multiply`：

```js
var multiply = function (x, y) {
  return x \* y;
};
```

一个命名为`func_named`的函数的函数表达式，被赋值给变量`multiply`：

```js
var multiply = function func\_name(x, y) {
  return x \* y;
};
```

### 差别

虽然有一些细微的差别，但所起的作用都差不多：

函数名和函数的变量存在着差别。函数名不能被改变，但函数的变量却能够被再分配。函数名只能在函数体内使用。倘若在函数体外使用函数名将会导致错误（如果函数之前是通过一个 var 语句声明的则是 undefined）。例如：

```js
var y = function x() {};
alert(x); // throws an error
```

当函数是通过 `Function`'s toString method被序列化时，函数名同样也会出现。

另一方面，被函数赋值的变量仅仅受限于它的作用域，该作用域确保包含着该函数被声明时的作用域。

正如第四个例子所展示的那样，函数名与被函数赋值的变量是不相同的。彼此之间没有关系。函数声明同时也创建了一个和函数名相同的变量。因此，与函数表达式定义不同，以函数声明定义的函数能够在它们被定义的作用域内通过函数名而被访问到：

使用用 '`new Function'定义的函数没有函数名。` 然而，在 SpiderMonkey JavaScript 引擎中，其函数的序列化形式表现的好像它拥有一个名叫"anonymous"的名称一样。比如，使用 `alert(new Function())` 输出：

```js
function anonymous() {}
```

而实际上其函数并没有名称，`anonymous` 不是一个可以在函数内被访问到的变量。例如，下面的例子将会导致错误：

```js
var foo = new Function("alert(anonymous);");
foo();
```

和通过函数表达式定义或者通过 Function 构造函数定义的函数不同，函数声明定义的函数可以在它被声明之前使用。举个例子：

```js
foo(); // alerts FOO!
function foo() {
  alert("FOO!");
}
```

函数表达式定义的函数继承了当前的作用域。换言之，函数构成了闭包。另一方面，Function 构造函数定义的函数不继承任何全局作用域以外的作用域 (那些所有函数都继承的)。

通过函数表达式定义的函数和通过函数声明定义的函数只会被解析一次，而 Function 构造函数定义的函数却不同。也就是说，每次构造函数被调用，传递给 Function 构造函数的函数体字符串都要被解析一次。虽然函数表达式每次都创建了一个闭包，但函数体不会被重复解析，因此函数表达式仍然要快于"`new Function(...)`"。所以 Function 构造函数应尽可能地避免使用。

有一点应该要注意的，在通过解析 Function 构造函数字符串产生的函数里，内嵌的函数表达式和函数声明不会被重复解析。例如：

```js
var foo = new Function(
  "var bar = 'FOO!';\nreturn(function() {\n\talert(bar);\n});",
)();
foo(); // 函数体字符串"function() {\n\talert(bar);\n}"的这一部分不会被重复解析。
```

函数声明非常容易（经常是意外地）转换为函数表达式。当它不再是一个函数声明：

* 成为表达式的一部分
* 不再是函数或者脚本自身的“源元素”（source element）。“源元素”是脚本或函数体中的非嵌套语句。

```js
var x = 0; // source element
if (x === 0) {
  // source element
  x = 10; // 非 source element
  function boo() {} // 非 source element
}
function foo() {
  // source element
  var y = 20; // source element
  function bar() {} // source element
  while (y === 10) {
    // source element
    function blah() {} // 非 source element
    y++; //非 source element
  }
}
```

### 示例

```js
// 函数声明
function foo() {}

// 函数表达式
(function bar() {});

// 函数表达式
x = function hello() {};

if (x) {
  // 函数表达式
  function world() {}
}

// 函数声明
function a() {
  // 函数声明
  function b() {}
  if (0) {
    //函数表达式
    function c() {}
  }
}
```

块级函数
----

从 ECMAScript 6 开始，在严格模式下，块里的函数作用域为这个块。ECMAScript 6 之前不建议块级函数在严格模式下使用。

```js
"use strict";

function f() {
  return 1;
}

{
  function f() {
    return 2;
  }
}

f() === 1; // true

// f() === 2 in non-strict mode
```

### 非严格模式下的块级函数

一句话：不要用。

在非严格模式下，块中的函数声明表现奇怪。例如：

```js
if (shouldDefineZero) {
  function zero() {
    // DANGER: 兼容性风险
    console.log("This is zero.");
  }
}
```

ECMAScript 6 中，如果`shouldDefineZero`是 false，则永远不会定义 zero，因为这个块从不执行。然而，这是标准的新的一部分。这是历史遗留问题，无论这个块是否执行，一些浏览器会定义 zero。

在严格模式 下，所有支持 `ECMAScript 6` 的浏览器以相同的方式处理：只有在 shouldDefineZero 为 `true` 的情况下定义 `zero`，并且作用域只是这个块内。

有条件地定义一个函数的一个更安全的方法是把函数表达式赋给一个变量：

```js
var zero;
if (0) {
  zero = function () {
    console.log("This is zero.");
  };
}
```

示例
--

### 返回格式化数字

下面的函数返回一个字符串，其中包含了一个格式化的、以一个由 0 开头并填充的数字。

```js
// 这个函数返回一个由 0 开头并填充的字符串
function padZeros(num, totalLen) {
  var numStr = num.toString(); // 用字符串返回值进行初始化
  var numZeros = totalLen - numStr.length; // 计算 zeros 顺序
  for (var i = 1; i <= numZeros; i++) {
    numStr = "0" + numStr;
  }
  return numStr;
}
```

下面的语句调用了 padZeros 函数：

```js
var result;
result = padZeros(42, 4); // returns "0042"
result = padZeros(42, 2); // returns "42"
result = padZeros(5, 4); // returns "0005"
```

### 检测函数是否存在

你可以通过 **typeof** 操作符检测一个函数是否存在。在下面的例子中，用一个测试来演示检测 window 对象是否拥有一个 noFunc 函数的属性。如果存在，那就使用它；否则就采取其他的一些操作。

```js
if ("function" === typeof window.noFunc) {
  // use noFunc()
} else {
  // do something else
}
```

注意在 if 语句中，使用了 noFunc 的引用——在函数名的后面没有括号“（）”，所以实际函数并没有被调用。

规范
--

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

浏览器兼容性
------

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

参阅
--

* `function statement`
* `function expression`
* `function* statement`
* `function* expression`
* `Function`
* `GeneratorFunction`
* `Arrow functions`
* `Default parameters`
* `Rest parameters`
* `Arguments object`
* `getter`
* `setter`
* `Method definitions`
* Functions and function scope


Uint32Array - JavaScript

Uint32Array
===========

**`Uint32Array`** 表示一个由基于平台字节序的 32 位无符号字节组成的数组。如果需要对字节顺序进行控制 (译者注：即 littleEndian 或 bigEndian)，请使用 `DataView` 代替。数组中每个元素的初始值都是`0`。一旦创建，你可以用对象的方法引用数组里的元素，或者使用标准的数组索引语法（即，使用中括号）。

语法
--
```

new Uint32Array(); // new in ES2017
new Uint32Array(length);
new Uint32Array(typedArray);
new Uint32Array(object);
new Uint32Array(buffer [, byteOffset [, length]]);
```

更多的构造器语法和属性请参照 *TypedArray。*

属性
--

`Uint32Array.BYTES_PER_ELEMENT`

返回一个数值，代表`Uint32Array`中单个元素的字节大小。`Uint32Array` 返回 `4`。

Uint32Array.length

固定值 (static) 属性，值为 3。使用 `Uint32Array.prototype.length` 获得数组的真实长度（元素个数）。

`Uint32Array.prototype` (en-US)

*TypedArray* 对象的原型链。

方法
--

`Uint32Array.from()`

从类似数组或者可迭代对象创建一个新的 `Uint32Array` 。请参考 `Array.from()`.

`Uint32Array.of()`

从可变长度的参数创建一个新的 `Uint32Array` 。请参考 `Array.of()`.

`Uint32Array` 原型
----------------

所有 `Uint32Array` 对象继承自 `%TypedArray%.prototype` (en-US).

### 属性

`Uint32Array.prototype.constructor`

返回创建实例原型的函数。默认返回 `Uint32Array` 的构造器。

`Uint32Array.prototype.buffer` 只读

返回 `Uint32Array`引用的 `ArrayBuffer`。由于构造时已固定，所以是**只读的**。

`Uint32Array.prototype.byteLength` 只读

返回从其 `ArrayBuffer` 开始的 `Uint32Array` 字节长度。由于构造时已固定，所以是**只读的**。

`Uint32Array.prototype.byteOffset` 只读

返回从其 `ArrayBuffer` 的偏移开始的 `Uint32Array` 字节长度。由于构造时已固定，所以是**只读的**。

`Uint32Array.prototype.length` 只读

返回 `Uint32Array` 中元素的个数。由于构造时已固定，所以是**只读的**。

### 方法

`Uint32Array.prototype.copyWithin()`

拷贝（浅拷贝）数组的部分元素到本数组的不同位置（不改变数组的大小）。请参考 `Array.prototype.copyWithin()`。

`Uint32Array.prototype.entries()`

返回一个 `Array Iterator` 对象，该对象包含数组中每一对索引的键值对。请参考 `Array.prototype.entries()`。

`Uint32Array.prototype.every()`

测试是否数组中的所有元素都通过给定的测试函数。请参考 `Array.prototype.every()`。

`Uint32Array.prototype.fill()`

使用静态值填充从起始下标到终止下标的数组元素。请参考 `Array.prototype.fill()`。

`Uint32Array.prototype.filter()`

创造一个新数组，含有原数组中可通过给定的过滤器函数的所有元素。请参考 `Array.prototype.filter()`。

`Uint32Array.prototype.find()`

如果数组中的元素满足提供的测试函数，返回找到的值，如果没有找到则返回 `undefined`。请参考 `Array.prototype.find()`。

`Uint32Array.prototype.findIndex()`

如果数组中的元素满足提供的测试函数，返回找到的下标，如果没有找到则返回 -1。请参考 `Array.prototype.findIndex()`。

`Uint32Array.prototype.forEach()`

对数组内的每个元素调用一个函数。请参考 `Array.prototype.forEach()`。

`Uint32Array.prototype.includes()`

判断该数组是否包含特定值，如果包含返回 `true`，否则返回`false`。请参考 `Array.prototype.includes()`。

`Uint32Array.prototype.indexOf()`

返回数组中等于特定值的第一个元素（下标最小）的下标，如果没有找到则返回 -1。请参考 `Array.prototype.indexOf()`。

`Uint32Array.prototype.join()`

将数组内的元素拼接成一个字符串。请参考 `Array.prototype.join()`。

`Uint32Array.prototype.keys()`

返回一个 `Array Iterator` 对象，该对象包含数组中所有索引 (key)。请参考 `Array.prototype.keys()`。

`Uint32Array.prototype.lastIndexOf()`

返回数组中等于特定值的最后一个元素（下标最大）的下标，如果没有找到则返回 -1。请参考 `Array.prototype.lastIndexOf()`。

`Uint32Array.prototype.map()`

用该数组的每个元素调用给定函数的结果创建新数组。请参考 `Array.prototype.map()`。

`Uint32Array.prototype.reduce()`

对累加器和数组的每个值应用函数（从左到右），使其归约为单一的值。请参考 `Array.prototype.reduce()`。

`Uint32Array.prototype.reduceRight()`

对累加器和数组的每个值应用函数（从右到左），使其归约为单一的值。请参考 `Array.prototype.reduceRight()`。

`Uint32Array.prototype.reverse()`

翻转数组中的元素顺序——首尾颠倒。请参考 `Array.prototype.reverse()`。

`Uint32Array.prototype.set()`

从一个给定的数组中读取多个数据并存储至 typed array。

`Uint32Array.prototype.slice()`

提取数组的某个部分并返回新的数组。请参考 `Array.prototype.slice()`。

`Uint32Array.prototype.some()`

测试是否数组中有（至少一个）元素可通过给定的测试函数。请参考 `Array.prototype.some()`。

`Uint32Array.prototype.sort()`

就地排序数组中的元素，并返回该数组。请参考 `Array.prototype.sort()`。

`Uint32Array.prototype.subarray()`

根据给定的起始和结束元素下标，返回一个新的 `Uint32Array` 子数组。

`Uint32Array.prototype.values()`

返回新的 `Array Iterator` 对象，其含有数组中每个索引的值。请参考 `Array.prototype.values()`。

`Uint32Array.prototype.toLocaleString()`

返回表示数组及其元素的本地化字符串。请参考 `Array.prototype.toLocaleString()`。

`Uint32Array.prototype.toString()`

返回表示数组及其元素的字符串。请参考 `Array.prototype.toString()`。

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

返回新的 `Array Iterator` 对象，其含有数组中每个索引的值。

示例
--

用不同的方法创建 `Uint32Array`：

```js
// 给定长度
var uint32 = new Uint32Array(2);
uint32[0] = 42;
console.log(uint32[0]); // 42
console.log(uint32.length); // 2
console.log(uint32.BYTES\_PER\_ELEMENT); // 4

// 给定数组
var arr = new Uint32Array([21, 31]);
console.log(arr[1]); // 31

// 给定 TypedArray
var x = new Uint32Array([21, 31]);
var y = new Uint32Array(x);
console.log(y[0]); // 21

// 给定 ArrayBuffer
var buffer = new ArrayBuffer(16);
var z = new Uint32Array(buffer, 0, 4);

// 给定可迭代对象
var iterable = (function\* () {
  yield\* [1, 2, 3];
})();
var uint32 = new Uint32Array(iterable);
// Uint32Array[1, 2, 3]
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # table-49 |

浏览器兼容性
------

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

参见
--

* JavaScript 类型化数组
* `ArrayBuffer`
* `DataView`


Object - JavaScript

Object
======

**`Object`** 是 JavaScript 的一种数据类型。它用于存储各种键值集合和更复杂的实体。可以通过 `Object()` 构造函数或者使用对象字面量的方式创建对象。

描述
--

在 JavaScript 中，几乎所有的对象都是 `Object` 的实例；一个典型的对象从 `Object.prototype` 继承属性（包括方法），尽管这些属性可能被覆盖（或者说重写）。唯一不从 `Object.prototype` 继承的对象是那些 `null` 原型对象，或者是从其他 `null` 原型对象继承而来的对象。

通过原型链，**所有**对象都能观察到 `Object.prototype` 对象的改变，除非这些改变所涉及的属性和方法沿着原型链被进一步重写。尽管有潜在的危险，但这为覆盖或扩展对象的行为提供了一个非常强大的机制。为了使其更加安全，`Object.prototype` 是核心 JavaScript 语言中唯一具有不可变原型的对象——`Object.prototype` 的原型始终为 `null` 且不可更改。

### 对象原型属性

你应该避免调用任何 `Object.prototype` 方法，特别是那些不打算多态化的方法（即只有其初始行为是合理的，且无法被任何继承的对象以合理的方式重写）。所有从 `Object.prototype` 继承的对象都可以自定义一个具有相同名称但语义可能与你的预期完全不同的自有属性。此外，这些属性不会被 `null` 原型对象继承。现代 JavaScript 中用于操作对象的工具方法都是静态的。更具体地说：

* `valueOf()`、`toString()` 和 `toLocaleString()` 存在的目的是为了多态化，你应该期望对象会定义自己的实现并具有合理的行为，因此你可以将它们作为实例方法调用。但是，`valueOf()` 和 `toString()` 通常是通过强制类型转换隐式调用的，因此你不需要在代码中自己调用它们。
* `__defineGetter__()`、`__defineSetter__()`、`__lookupGetter__()` 和 `__lookupSetter__()` 已被弃用，不应该再使用。请使用静态方法 `Object.defineProperty()` 和 `Object.getOwnPropertyDescriptor()` 作为替代。
* `__proto__` 属性已被弃用，不应该再使用。请使用静态方法 `Object.getPrototypeOf()` 和 `Object.setPrototypeOf()` 作为替代。
* `propertyIsEnumerable()` 和 `hasOwnProperty()` 方法可以分别用静态方法 `Object.getOwnPropertyDescriptor()` 和 `Object.hasOwn()` 替换。
* 如果你正在检查一个构造函数的 `prototype` 属性，通常可以用 `instanceof` 代替 `isPrototypeOf()` 方法。

如果不存在语义上等价的静态方法，或者你真的想使用 `Object.prototype` 方法，你应该通过 `call()` 直接在目标对象上调用 `Object.prototype` 方法，以防止因目标对象上原有方法被重写而产生意外的结果。

```js
const obj = {
  foo: 1,
  // 如果可能的话，你不应该在自己的对象上定义这样的方法，
  // 但是如果你从外部输入接收对象，可能无法防止这种情况的发生
  propertyIsEnumerable() {
    return false;
  },
};

obj.propertyIsEnumerable("foo"); // false；预期外的结果
Object.prototype.propertyIsEnumerable.call(obj, "foo"); // true；预期的结果
```

### 从对象中删除属性

一个对象本身没有任何方法可以（像 `Map.prototype.delete()` 一样）删除自己的属性。要删除一个对象的属性，必须使用 delete 运算符。

### null 原型对象

几乎所有的 JavaScript 对象最终都继承自 `Object.prototype`（参见继承和原型链）。然而，你可以使用 `Object.create(null)` 或定义了 `__proto__: null` 的对象字面量语法（注意：对象字面量中的 `__proto__` 键不同于已弃用的 `Object.prototype.__proto__` 属性）来创建 `null` 原型对象。你还可以通过调用 `Object.setPrototypeOf(obj, null)` 将现有对象的原型更改为 `null`。

```js
const obj = Object.create(null);
const obj2 = { \_\_proto\_\_: null };
```

`null` 原型对象可能会有一些预期外的行为表现，因为它不会从 `Object.prototype` 继承任何对象方法。这在调试时尤其需要注意，因为常见的对象属性转换/检测实用方法可能会产生错误或丢失信息（特别是在使用了忽略错误的静默错误捕获机制的情况下）。

例如，`Object.prototype.toString()` 方法的缺失通常会使得调试变得困难：

```js
const normalObj = {}; // 创建一个普通对象
const nullProtoObj = Object.create(null); // 创建一个 "null" 原型对象

console.log(`normalObj 是：${normalObj}`); // 显示 "normalObj 是：[object Object]"
console.log(`nullProtoObj 是：${nullProtoObj}`); // 抛出错误：Cannot convert object to primitive value

alert(normalObj); // 显示 [object Object]
alert(nullProtoObj); // 抛出错误：Cannot convert object to primitive value
```

其他方法也会失败。

```js
normalObj.valueOf(); // 显示 {}
nullProtoObj.valueOf(); // 抛出错误：nullProtoObj.valueOf is not a function

normalObj.hasOwnProperty("p"); // 显示 "true"
nullProtoObj.hasOwnProperty("p"); // 抛出错误：nullProtoObj.hasOwnProperty is not a function

normalObj.constructor; // 显示 "Object() { [native code] }"
nullProtoObj.constructor; // 显示 "undefined"
```

我们可以通过为 `null` 原型对象分配属性的方式将 `toString` 方法添加回去：

```js
nullProtoObj.toString = Object.prototype.toString; // 由于新对象缺少 `toString` 方法，因此需要将原始的通用 `toString` 方法添加回来。

console.log(nullProtoObj.toString()); // 显示 "[object Object]"
console.log(`nullProtoObj 是：${nullProtoObj}`); // 显示 "nullProtoObj 是：[object Object]"
```

普通对象的 `toString()` 方法是在对象的原型上的，而与普通对象不同的是，这里的 `toString()` 方法是 `nullProtoObj` 的自有属性。这是因为 `nullProtoObj` 没有原型（即为 `null`）。

在实践中，`null` 原型对象通常被用作 map 的简单替代品。由于存在 `Object.prototype` 属性，会导致一些错误：

```js
const ages = { alice: 18, bob: 27 };

function hasPerson(name) {
  return name in ages;
}

function getAge(name) {
  return ages[name];
}

hasPerson("hasOwnProperty"); // true
getAge("toString"); // [Function: toString]
```

使用一个 `null` 原型对象可以消除这种风险，同时不会令 `hasPerson` 和 `getAge` 函数变得复杂：

```js
const ages = Object.create(null, {
  alice: { value: 18, enumerable: true },
  bob: { value: 27, enumerable: true },
});

hasPerson("hasOwnProperty"); // false
getAge("toString"); // undefined
```

在这种情况下，添加任何方法都应该慎重，因为它们可能会与存储为数据的其他键值对混淆。

让你的对象不继承自 `Object.prototype` 还可以防止原型污染攻击。如果恶意脚本向 `Object.prototype` 添加一个属性，程序中的每个对象上都可访问它，除了那些原型为 `null` 的对象。

```js
const user = {};

// 恶意脚本：
Object.prototype.authenticated = true;

// 意外允许未经身份验证的用户通过
if (user.authenticated) {
  // 访问机密数据
}
```

JavaScript 还具有内置的 API，用于生成 `null` 原型对象，特别是那些将对象用作临时键值对集合的 API。例如：

* `Object.groupBy()` 方法的返回值
* `RegExp.prototype.exec()` 方法返回结果中的 `groups` 和 `indices.groups` 属性
* `Array.prototype[@@unscopables]` 属性（所有 `@@unscopables` 对象原型都应该为 `null`）
* `import.meta` 对象
* 通过 `import * as ns from "module"` 或 `import()` (en-US) 获取的模块命名空间对象

“`null` 原型对象”这个术语通常也包括其原型链中没有 `Object.prototype` 的任何对象。当使用类时，可以通过 `extends null` 来创建这样的对象。

### 对象强制转换

许多内置操作首先将它们的参数强制转换为对象。该过程可以概括如下：

* 对象则按原样返回。
* `undefined` 和 `null` 则抛出 `TypeError`。
* `Number`、`String`、`Boolean`、`Symbol`、`BigInt` 等基本类型被封装成其对应的基本类型对象。

在 JavaScript 中实现相同效果的最佳方式是使用 `Object()` 构造函数。`Object(x)` 可以将 `x` 转换为对象，对于 `undefined` 或 `null`，它会返回一个普通对象而不是抛出 `TypeError` 异常。

使用对象强制转换的地方包括：

* `for...in` 循环的 `object` 参数。
* `Array` 方法的 `this` 值。
* `Object` 方法的参数，如 `Object.keys()`。
* 当访问基本类型的属性时进行自动转换，因为基本类型没有属性。
* 在调用非严格函数时的 `this` 值。基本类型值被封装为对象，而 `null` 和 `undefined` 被替换为全局对象。

与转换为基本类型不同，对象强制转换过程本身无法以任何方式被观察到，因为它不会调用像 `toString` 或 `valueOf` 方法这样的自定义代码。

构造函数
----

`Object()`

将输入转换为一个对象。

静态方法
----

`Object.assign()`

将一个或多个源对象的所有可枚举自有属性的值复制到目标对象中。

`Object.create()`

使用指定的原型对象和属性创建一个新对象。

`Object.defineProperties()`

向对象添加多个由给定描述符描述的命名属性。

`Object.defineProperty()`

向对象添加一个由给定描述符描述的命名属性。

`Object.entries()`

返回包含给定对象**自有**可枚举字符串属性的所有 `[key, value]` 数组。

`Object.freeze()`

冻结一个对象。其他代码不能删除或更改其任何属性。

`Object.fromEntries()`

从一个包含 `[key, value]` 对的可迭代对象中返回一个新的对象（`Object.entries` 的反操作）。

`Object.getOwnPropertyDescriptor()`

返回一个对象的已命名属性的属性描述符。

`Object.getOwnPropertyDescriptors()`

返回一个包含对象所有自有属性的属性描述符的对象。

`Object.getOwnPropertyNames()`

返回一个包含给定对象的所有**自有**可枚举和不可枚举属性名称的数组。

`Object.getOwnPropertySymbols()`

返回一个数组，它包含了指定对象所有自有 symbol 属性。

`Object.getPrototypeOf()`

返回指定对象的原型（内部的 `[[Prototype]]` 属性）。

`Object.hasOwn()`

如果指定属性是指定对象的自有属性，则返回 `true`，否则返回 `false`。如果该属性是继承的或不存在，则返回 `false`。

`Object.is()`

比较两个值是否相同。所有 `NaN` 值都相等（这与 `==` 使用的 `IsLooselyEqual` 和 `===` 使用的 `IsStrictlyEqual` 不同）。

`Object.isExtensible()`

判断对象是否可扩展。

`Object.isFrozen()`

判断对象是否已经冻结。

`Object.isSealed()`

判断对象是否已经封闭。

`Object.keys()`

返回一个包含所有给定对象**自有**可枚举字符串属性名称的数组。

`Object.preventExtensions()`

防止对象的任何扩展。

`Object.seal()`

防止其他代码删除对象的属性。

`Object.setPrototypeOf()`

设置对象的原型（即内部 `[[Prototype]]` 属性）。

`Object.values()`

返回包含给定对象所有**自有**可枚举字符串属性的值的数组。

实例属性
----

这些属性在 `Object.prototype` 上定义，被所有 `Object` 实例所共享。

`Object.prototype.__proto__`
已弃用

指向实例对象在实例化时使用的原型对象。

`Object.prototype.constructor`

创建该实例对象的构造函数。对于普通的 `Object` 实例，初始值为 `Object` 构造函数。其它构造函数的实例都会从它们各自的 `Constructor.prototype` 对象中继承 `constructor` 属性。

实例方法
----

`Object.prototype.__defineGetter__()`
已弃用

将一个属性与一个函数相关联，当该属性被访问时，执行该函数，并且返回函数的返回值。

`Object.prototype.__defineSetter__()`
已弃用

将一个属性与一个函数相关联，当该属性被设置时，执行该函数，执行该函数去修改某个属性。

`Object.prototype.__lookupGetter__()`
已弃用

返回绑定在指定属性上的 getter 函数。

`Object.prototype.__lookupSetter__()`
已弃用

返回绑定在指定属性上的 setter 函数。

`Object.prototype.hasOwnProperty()`

返回一个布尔值，用于表示一个对象自身是否包含指定的属性，该方法并不会查找原型链上继承来的属性。

`Object.prototype.isPrototypeOf()`

返回一个布尔值，用于表示该方法所调用的对象是否在指定对象的原型链中。

`Object.prototype.propertyIsEnumerable()`

返回一个布尔值，指示指定属性是否是对象的可枚举自有属性。

`Object.prototype.toLocaleString()`

调用 `toString()` 方法。

`Object.prototype.toString()`

返回一个代表该对象的字符串。

`Object.prototype.valueOf()`

返回指定对象的基本类型值。

示例
--

### 构造空对象

以下示例使用带有不同参数的 `new` 关键字创建空对象：

```js
const o1 = new Object();
const o2 = new Object(undefined);
const o3 = new Object(null);
```

### 使用 `Object` 生成布尔对象

下面的例子将 `Boolean` 对象存到 `o` 中：

```js
// 等价于 const o = new Boolean(true);
const o = new Object(true);
```

```js
// 等价于 const o = new Boolean(false);
const o = new Object(Boolean());
```

### Object prototype

当我们要修改现有的 `Object.prototype` 方法时，请你考虑一下在现有逻辑之前或者之后通过包装扩展代码的方式来注入代码。例如，以下（未经测试的）代码将会在执行内部逻辑或者是其他扩展之前，有条件地执行一段自定义的逻辑。

在使用钩子修改原型时，通过在函数上调用 `apply()` 方法并传递 `this` 和参数（即调用状态），将其传递给当前行为。这种模式可以用于任何原型，例如 `Node.prototype`、`Function.prototype` 等。

```js
const current = Object.prototype.valueOf;

// 由于我的属性“-prop-value”是横跨多个原型链的，并且不总是在同一个原型链上，
// 因此我想修改 Object.prototype：
Object.prototype.valueOf = function (...args) {
  if (Object.hasOwn(this, "-prop-value")) {
    return this["-prop-value"];
  } else {
    // 这似乎不是我的对象，因此让我们尽可能实现默认行为。
    // 在某些其他语言中，apply 的行为类似于 "super"。
    // 即使 valueOf() 不需要参数，但其他的方法可能需要参数。
    return current.apply(this, args);
  }
};
```

**警告：** 修改任何内置构造函数的 `prototype` 属性被认为是一种不好的做法，可能会影响向前兼容性。

你可以阅读更多关于原型的内容，参见继承与原型链。

规范
--

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

浏览器兼容性
------

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

参见
--

* 初始化对象


Int8Array - JavaScript

Int8Array
=========

**`Int8Array`** 类型数组表示二进制补码 8 位有符号整数的数组。内容初始化为 0。一旦建立，你可以使用对象的方法引用数组中的元素，或使用标准数组索引语法 ( 即，使用括号注释)。

语法
--
```

new Int8Array(length);
new Int8Array(typedArray);
new Int8Array(object);
new Int8Array(buffer [, byteOffset [, length]]);
```

有关构造函数语法和参数的更多信息，请访问 *TypedArray*.

属性
--

`Int8Array.BYTES_PER_ELEMENT`

返回数组中每个元素的大小。在 `Int8Array` 中这个值为 1。

Int8Array.length

此属性为固定值属性，值为 3。查看 `Int8Array.prototype.length` 获得获取数组内元素个数方法。

`Int8Array.prototype` (en-US)

*TypedArray* 对象的构造原型。

方法
--

`Int8Array.from()`

从类数组对象或迭代器生成 int8Array 数组对象。参照 `Array.from()`

`Int8Array.of()`

以多个参数构造 Int8Array 对象，参照 `Array.of()`

`Int8Array` 原型方法
----------------

所有 **Int8Array** 对象都继承自 `%TypedArray%.prototype` (en-US).

### 属性

`Int8Array.prototype.constructor`

这个方法会返回对象的构造原型。默认为`Int8Array` 构造函数。

`Int8Array.prototype.buffer` 只读

Returns the `ArrayBuffer` referenced by the `Int8Array` Fixed at construction time and thus **read only**.

`Int8Array.prototype.byteLength` 只读

Returns the length (in bytes) of the `Int8Array` from the start of its `ArrayBuffer`. Fixed at construction time and thus **read only.**

`Int8Array.prototype.byteOffset` 只读

Returns the offset (in bytes) of the `Int8Array` from the start of its `ArrayBuffer`. Fixed at construction time and thus **read only.**

`Int8Array.prototype.length` 只读

Returns the number of elements hold in the `Int8Array`. Fixed at construction time and thus **read only.**

### 方法

`Int8Array.prototype.copyWithin()`

Copies a sequence of array elements within the array. See also `Array.prototype.copyWithin()`.

`Int8Array.prototype.entries()`

Returns a new `Array Iterator` object that contains the key/value pairs for each index in the array. See also `Array.prototype.entries()`.

`Int8Array.prototype.every()`

Tests whether all elements in the array pass the test provided by a function. See also `Array.prototype.every()`.

`Int8Array.prototype.fill()`

Fills all the elements of an array from a start index to an end index with a static value. See also `Array.prototype.fill()`.

`Int8Array.prototype.filter()`

Creates a new array with all of the elements of this array for which the provided filtering function returns true. See also `Array.prototype.filter()`.

`Int8Array.prototype.find()`

Returns the found value in the array, if an element in the array satisfies the provided testing function or `undefined` if not found. See also `Array.prototype.find()`.

`Int8Array.prototype.findIndex()`

Returns the found index in the array, if an element in the array satisfies the provided testing function or -1 if not found. See also `Array.prototype.findIndex()`.

`Int8Array.prototype.forEach()`

Calls a function for each element in the array. See also `Array.prototype.forEach()`.

`Int8Array.prototype.includes()`
实验性

Determines whether a typed array includes a certain element, returning `true` or `false` as appropriate. See also `Array.prototype.includes()`.

`Int8Array.prototype.indexOf()`

Returns the first (least) index of an element within the array equal to the specified value, or -1 if none is found. See also `Array.prototype.indexOf()`.

`Int8Array.prototype.join()`

Joins all elements of an array into a string. See also `Array.prototype.join()`.

`Int8Array.prototype.keys()`

Returns a new `Array Iterator` that contains the keys for each index in the array. See also `Array.prototype.keys()`.

`Int8Array.prototype.lastIndexOf()`

Returns the last (greatest) index of an element within the array equal to the specified value, or -1 if none is found. See also `Array.prototype.lastIndexOf()`.

`Int8Array.prototype.map()`

返回一个由回调函数的返回值组成的新数组。. See also `Array.prototype.map()`.

`Int8Array.prototype.move()`
非标准
 未实现

`Int8Array.prototype.copyWithin()` 早期的不标准定义。

`Int8Array.prototype.reduce()`

从左到右为每个数组元素执行一次回调函数，并把上次回调函数的返回值放在一个暂存器中传给下次回调函数，并返回最后一次回调函数的返回值。参照 `Array.prototype.reduce()`

`Int8Array.prototype.reduceRight()`

从右到左为每个数组元素执行一次回调函数，并把上次回调函数的返回值放在一个暂存器中传给下次回调函数，并返回最后一次回调函数的返回值。参照 `Array.prototype.reduceRight()`

`Int8Array.prototype.reverse()`

颠倒数组中元素的排列顺序，即原先的第一个变为最后一个，原先的最后一个变为第一个。参照 `Array.prototype.reverse()`

`Int8Array.prototype.set()`

Stores multiple values in the typed array, reading input values from a specified array.

`Int8Array.prototype.slice()`

抽取当前数组中的一段元素组合成一个新数组。参照 `Array.prototype.slice()`。

`Int8Array.prototype.some()`

如果数组中至少有一个元素满足测试函数，则返回 true，否则返回 false。参照 `Array.prototype.some()`

`Int8Array.prototype.sort()`

对数组元素进行排序，并返回当前数组。参照`Array.prototype.sort()`.

`Int8Array.prototype.subarray()`

返回一个以给定的初始结束位置裁剪的 Int8Array 数组。

`Int8Array.prototype.values()`

返回一个数组迭代器对象，该迭代器会包含所有数组元素的值。参照 `Array.prototype.values()`

`Int8Array.prototype.toLocaleString()`

返回一个由所有数组元素组合而成的本地化后的字符串。See also `Array.prototype.toLocaleString()`.

`Int8Array.prototype.toString()`

返回一个由所有数组元素组合而成的字符串。参照 `Array.prototype.toString()`

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

此方法返回一个带有数组内所有元素的迭代器对象，同 `Int8Array.prototype.values`

示例
--

```js
// 以长度参数构造对象
var int8 = new Int8Array(2);
int8[0] = 42;
console.log(int8[0]); // 42
console.log(int8.length); // 2
console.log(int8.BYTES\_PER\_ELEMENT); // 1

// 以数组构造对象
var arr = new Int8Array([21, 31]);
console.log(arr[1]); // 31

// 从另一数组构造对象
var x = new Int8Array([21, 31]);
var y = new Int8Array(x);
console.log(y[0]); // 21

// 从 ArrayBuffer 构造对象
var buffer = new ArrayBuffer(8);
var z = new Int8Array(buffer, 1, 4);
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # table-49 |

浏览器兼容性
------

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

兼容性说明
-----

自 ECMAScript 2015 (ES6) 施行，`Int8Array` 需要使用 `new` 构造。从当前版本开始，不加 new 而便调用 `Int8Array` 构造器方法，将报出 `TypeError` 错误。

```js
var dv = Int8Array([1, 2, 3]);
// TypeError: calling a builtin Int8Array constructor
// without new is forbidden
```

```js
var dv = new Int8Array([1, 2, 3]);
```

参见
--

* `core-js` 中 `Int8Array` 的 polyfill
* JavaScript 类型化数组指南
* `TypedArray`
* `ArrayBuffer`
* `DataView`


Symbol - JavaScript

Symbol
======

**symbol** 是一种基本数据类型（primitive data type）。`Symbol()` 函数会返回 **symbol** 类型的值，该类型具有静态属性和静态方法。它的静态属性会暴露几个内建的成员对象；它的静态方法会暴露全局的 symbol 注册，且类似于内建对象类，但作为构造函数来说它并不完整，因为它不支持语法："`new Symbol()`"。

每个从 `Symbol()` 返回的 symbol 值都是唯一的。一个 symbol 值能作为对象属性的标识符；这是该数据类型仅有的目的。更进一步的解析见——glossary entry for Symbol。

尝试一下
----

语法
--
```

Symbol([description])
```

### 参数

`description` 可选

可选的，字符串类型。对 symbol 的描述，可用于调试但不是访问 symbol 本身。

描述
--

直接使用`Symbol()`创建新的 symbol 类型，并用一个可选的字符串作为其描述。

```js
var sym1 = Symbol();
var sym2 = Symbol("foo");
var sym3 = Symbol("foo");
```

上面的代码创建了三个新的 symbol 类型。注意，`Symbol("foo")` 不会强制将字符串“foo”转换成 symbol 类型。它每次都会创建一个新的 symbol 类型：

```js
Symbol("foo") === Symbol("foo"); // false
```

下面带有 `new` 运算符的语法将抛出 `TypeError` 错误：

```js
var sym = new Symbol(); // TypeError
```

这会阻止创建一个显式的 Symbol 包装器对象而不是一个 Symbol 值。围绕原始数据类型创建一个显式包装器对象从 ECMAScript 6 开始不再被支持。然而，现有的原始包装器对象，如 `new Boolean`、`new String`以及`new Number`，因为遗留原因仍可被创建。

如果你真的想创建一个 **Symbol 包装器对象** (`Symbol wrapper object`)，你可以使用 `Object()` 函数：

```js
var sym = Symbol("foo");
typeof sym; // "symbol"
var symObj = Object(sym);
typeof symObj; // "object"
```

### 全局共享的 Symbol

上面使用 `Symbol()` 函数的语法，不会在你的整个代码库中创建一个可用的全局的 symbol 类型。要创建跨文件可用的 symbol，甚至跨域（每个都有它自己的全局作用域），使用 `Symbol.for()` 方法和 `Symbol.keyFor()` 方法从全局的 symbol 注册表设置和取得 symbol。

### 在对象中查找 Symbol 属性

`Object.getOwnPropertySymbols()` 方法让你在查找一个给定对象的符号属性时返回一个 symbol 类型的数组。注意，每个初始化的对象都是没有自己的 symbol 属性的，因此这个数组可能为空，除非你已经在对象上设置了 symbol 属性。

属性
--

`Symbol.length`

长度属性，值为 0。

`Symbol.prototype` (en-US)

`symbol` 构造函数的原型。

### 内置通用（well-known）symbol

除了自己创建的 symbol，JavaScript 还内建了一些在 ECMAScript 5 之前没有暴露给开发者的 symbol，它们代表了内部语言行为。它们可以使用以下属性访问：

迭代 symbols `Symbol.iterator`

一个返回一个对象默认迭代器的方法。被 `for...of` 使用。

`Symbol.asyncIterator`
实验性

一个返回对象默认的异步迭代器的方法。被 `for await of` 使用。

正则表达式 symbols `Symbol.match`

一个用于对字符串进行匹配的方法，也用于确定一个对象是否可以作为正则表达式使用。被 `String.prototype.match()` 使用。

`Symbol.replace`

一个替换匹配字符串的子串的方法。被 `String.prototype.replace()` 使用。

`Symbol.search`

一个返回一个字符串中与正则表达式相匹配的索引的方法。被 `String.prototype.search()` 使用。

`Symbol.split`

一个在匹配正则表达式的索引处拆分一个字符串的方法.。被 `String.prototype.split()` 使用。

其他 symbols `Symbol.hasInstance`

一个确定一个构造器对象识别的对象是否为它的实例的方法。被 `instanceof` 使用。

`Symbol.isConcatSpreadable`

一个布尔值，表明一个对象是否应该 flattened 为它的数组元素。被 `Array.prototype.concat()` 使用。

`Symbol.unscopables`

拥有和继承属性名的一个对象的值被排除在与环境绑定的相关对象外。

`Symbol.species`

一个用于创建派生对象的构造器函数。

`Symbol.toPrimitive`

一个将对象转化为基本数据类型的方法。

`Symbol.toStringTag`

用于对象的默认描述的字符串值。被 `Object.prototype.toString()` 使用。

方法
--

`Symbol.for(key)`

使用给定的 key 搜索现有的 symbol，如果找到则返回该 symbol。否则将使用给定的 key 在全局 symbol 注册表中创建一个新的 symbol。

`Symbol.keyFor(sym)`

从全局 symbol 注册表中，为给定的 symbol 检索一个共享的？symbol key。

Symbol 原型
---------

所有 Symbols 继承自 `Symbol.prototype` (en-US).

### 实例属性

`Symbol.prototype.description`

一个只读的字符串，意为对该 Symbol 对象的描述

### 实例方法

`Symbol.prototype.toSource`

返回该 Symbol 对象的源代码。该方法重写了 `Object.prototype.toSource` 方法

`Symbol.prototype.toString`

返回一个包含着该 Symbol 对象描述的字符串。该方法重写了 `Object.prototype.toString` 方法

`Symbol.prototype.valueOf`

返回该 Symbol 对象。该方法重写了 `Object.prototype.valueOf` 方法

`Symbol.prototype[@@toPrimitive]`

返回该 Symbol 对象。

示例
--

### 对 symbol 使用 typeof 运算符

`typeof`运算符能帮助你识别 symbol 类型

```js
typeof Symbol() === "symbol";
typeof Symbol("foo") === "symbol";
typeof Symbol.iterator === "symbol";
```

### Symbol 类型转换

当使用 symbol 值进行类型转换时需要注意一些事情：

* 尝试将一个 symbol 值转换为一个 number 值时，会抛出一个 `TypeError` 错误 (e.g. `+sym` or `sym | 0`).
* 使用宽松相等时，`Object(sym) == sym` returns `true.`
* 这会阻止你从一个 symbol 值隐式地创建一个新的 string 类型的属性名。例如，`Symbol("foo") + "bar"` 将抛出一个 `TypeError` (can't convert symbol to string).
* "safer" `String(sym)` conversion 的作用会像 symbol 类型调用 `Symbol.prototype.toString()` 一样，但是注意 `new String(sym)` 将抛出异常。
### Symbols 与 `for...in` 迭代

Symbols 在 `for...in` 迭代中不可枚举。另外，`Object.getOwnPropertyNames()` 不会返回 symbol 对象的属性，但是你能使用 `Object.getOwnPropertySymbols()` 得到它们。

```js
var obj = {};

obj[Symbol("a")] = "a";
obj[Symbol.for("b")] = "b";
obj["c"] = "c";
obj.d = "d";

for (var i in obj) {
  console.log(i); // logs "c" and "d"
}
```

### Symbols 与 `JSON.stringify()`

当使用 JSON.stringify() 时，以 symbol 值作为键的属性会被完全忽略：

```js
JSON.stringify({ [Symbol("foo")]: "foo" });
// '{}'
```

更多细节，请看 `JSON.stringify()`。

### Symbol 包装器对象作为属性的键

当一个 Symbol 包装器对象作为一个属性的键时，这个对象将被强制转换为它包装过的 symbol 值：

```js
var sym = Symbol("foo");
var obj = { [sym]: 1 };
obj[sym]; // 1
obj[Object(sym)]; // still 1
```

规范
--

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

浏览器兼容性
------

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

参考文档
----

* Glossary: Symbol data type
* `typeof`
* Data types and data structures
* "ES6 In Depth: Symbols"（深入 ES6 Symbols）on hacks.mozilla.org