Array - JavaScript

Array
=====

与其他编程语言中的数组一样，**`Array`** 对象支持在单个变量名下存储多个元素，并具有执行常见数组操作的成员。

描述
--

在 JavaScript 中，数组不是基本类型，而是具有以下核心特征的 `Array` 对象：

* **JavaScript 数组是可调整大小的，并且可以包含不同的数据类型**。（当不需要这些特征时，可以使用类型化数组。）
* **JavaScript 数组不是关联数组**，因此，不能使用任意字符串作为索引访问数组元素，但必须使用非负整数（或它们各自的字符串形式）作为索引访问。
* **JavaScript 数组的索引从 0 开始**：数组的第一个元素在索引 `0` 处，第二个在索引 `1` 处，以此类推，最后一个元素是数组的 `length` 属性减去 `1` 的值。
* **JavaScript 数组复制操作创建浅拷贝**。（*所有* JavaScript 对象的标准内置复制操作都会创建浅拷贝，而不是深拷贝）。
### 数组下标

`Array` 对象不能使用任意字符串作为元素索引（如关联数组），必须使用非负整数（或它们的字符串形式）。通过非整数设置或访问不会设置或从数组列表本身检索元素，但会设置或访问与该数组的对象属性集合相关的变量。数组的对象属性和数组元素列表是分开的，数组的遍历和修改操作不能应用于这些命名属性。

数组元素是对象属性，就像 `toString` 是属性一样（具体来说，`toString()` 是一种方法）。然而，尝试按以下方式访问数组的元素会抛出语法错误，因为属性名无效：

```js
console.log(arr.0); // 语法错误
```

JavaScript 语法要求使用方括号表示法而不是点号表示法来访问以数字开头的属性。也可以用引号包裹数组下标（例如，`years['2']` 而不是 `years[2]`），尽管通常没有必要。

JavaScript 引擎通过隐式的 `toString`，将 `years[2]` 中的 `2` 强制转换为字符串。因此，`'2'` 和 `'02'` 将指向 `years` 对象上的两个不同的槽位，下面的例子可能是 `true`：

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

只有 `years['2']` 是一个实际的数组索引。`years['02']` 是一个在数组迭代中不会被访问的任意字符串属性。

### 长度与数值属性的关系

JavaScript 数组的 `length` 属性和数值属性是连接的。

一些内置数组方法（例如 `join()`、`slice()`、`indexOf()` 等）在被调用时会考虑到数组的 `length` 属性的值。

其他方法（例如，`push()`、`splice()` 等）也会更新数组的 `length` 属性。

```js
const fruits = [];
fruits.push("banana", "apple", "peach");
console.log(fruits.length); // 3
```

当在 JavaScript 数组上设置一个属性时，如果该属性是一个有效的数组索引并且该索引在数组的当前边界之外，引擎将相应地更新数组的 `length` 属性：

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

增加 `length`。

```js
fruits.length = 10;
console.log(fruits); // ['banana', 'apple', 'peach', empty x 2, 'mango', empty x 4]
console.log(Object.keys(fruits)); // ['0', '1', '2', '5']
console.log(fruits.length); // 10
console.log(fruits[8]); // undefined
```

但是，减少 `length` 属性会删除元素。

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

这将在 `Array/length` 页中进一步解释。

### 数组方法和空槽

稀疏数组中的空槽在数组方法之间的行为不一致。通常，旧方法会跳过空槽，而新方法将它们视为 `undefined`。

在遍历多个元素的方法中，下面的方法在访问索引之前执行 `in` 检查，并且不将空槽与 `undefined` 合并：

* `concat()`
* `copyWithin()`
* `every()`
* `filter()`
* `flat()`
* `flatMap()`
* `forEach()`
* `indexOf()`
* `lastIndexOf()`
* `map()`
* `reduce()`
* `reduceRight()`
* `reverse()`
* `slice()`
* `some()`
* `sort()`
* `splice()`

关于它们是如何处理空槽的，请参阅每个方法的页面。

这些方法将空槽视为 `undefined`：

* `entries()`
* `fill()`
* `find()`
* `findIndex()`
* `findLast()`
* `findLastIndex()`
* `includes()`
* `join()`
* `keys()`
* `toLocaleString()`
* `values()`
### 复制方法和修改方法

有些方法不会修改调用该方法的现有数组，而是返回一个新的数组。它们通过首先构造一个新数组，然后填充元素来实现。复制始终是*浅层次的*——该方法从不复制一开始创建的数组之外的任何内容。原始数组的元素将按以下方式复制到新数组中：

* 对象：对象引用被复制到新数组中。原数组和新数组都引用同一个对象。也就是说，如果一个被引用的对象被修改，新数组和原数组都可以看到更改。
* 基本类型，如字符串、数字和布尔值（不是 `String`、`Number` 和 `Boolean` 对象）：它们的值被复制到新数组中。

其他方法会改变调用该方法的数组，在这种情况下，它们的返回值根据方法的不同而不同：有时是对相同数组的引用，有时是新数组的长度。

以下方法通过访问 `this.constructor[Symbol.species]` 来创建新数组，以确定要使用的构造函数：

* `concat()`
* `filter()`
* `flat()`
* `flatMap()`
* `map()`
* `slice()`
* `splice()`（构造返回的已删除元素数组）

以下方法总是使用 `Array` 基础构造函数创建新数组：

* `toReversed()`
* `toSorted()`
* `toSpliced()`
* `with()`

下表列出了会修改原始数组的方法，以及相应的非修改方法：

| 修改方法 | 相应的非修改方法 |
| --- | --- |
| `copyWithin()` | 没有相应的非修改方法 |
| `fill()` | 没有相应的非修改方法 |
| `pop()` | `slice(0, -1)` |
| `push(v1, v2)` | `concat([v1, v2])` |
| `reverse()` | `toReversed()` |
| `shift()` | `slice(1)` |
| `sort()` | `toSorted()` |
| `splice()` | `toSpliced()` |
| `unshift(v1, v2)` | `toSpliced(0, 0, v1, v2)` |

将改变原数组的方法转换为非修改方法的一种简单方式是使用展开语法或 `slice()` 先创建一个副本：

```js
arr.copyWithin(0, 1, 2); // 改变了 arr
const arr2 = arr.slice().copyWithin(0, 1, 2); // 不改变 arr
const arr3 = [...arr].copyWithin(0, 1, 2); // 不改变 arr
```

### 迭代方法

许多数组方法接受一个回调函数作为参数。回调函数按顺序为数组中的每个元素调用，且最多调用一次，并且回调函数的返回值用于确定方法的返回值。它们都具有相同的方法签名：

```js
method(callbackFn, thisArg)
```

其中 `callbackFn` 接受三个参数

`element`

数组中当前正在处理的元素。

`index`

正在处理的元素在数组中的索引。

`array`

调用该方法的数组。

`callbackFn` 的返回值取决于调用的数组方法。

`thisArg` 参数（默认为 `undefined`）将在调用 `callbackFn` 时用作 `this` 值。最终由 `callbackFn` 观察到的 `this` 值根据通常的规则 确定：如果 `callbackFn` 是非严格模式（译注：正常模式/马虎模式），原始 `this` 值将被包装为对象，并将 `undefined`/`null` 替换为 `globalThis`。对于使用 箭头函数 定义的任何 `callbackFn` 来说，`thisArg` 参数都是无关紧要的，因为箭头函数没有自己的 `this` 绑定。

所有迭代方法都是复制方法和通用方法，尽管它们在处理空槽时的行为不同。

以下方法是迭代方法：

* `every()`
* `filter()`
* `find()`
* `findIndex()`
* `findLast()`
* `findLastIndex()`
* `flatMap()`
* `forEach()`
* `map()`
* `some()`

特别地，`every()`、`find()`、`findIndex()`、`findLast()`、`findLastIndex()` 和 `some()` 并不总是在每个元素上调用 `callbackFn`——它们在确定返回值后立即停止迭代。

还有两个方法接受一个回调函数，并对数组中的每个元素最多运行一次，但它们的方法签名与典型的迭代方法略有不同（例如，它们不接受 `thisArg`）：

* `reduce()`
* `reduceRight()`

`sort()` 方法也接受一个回调函数，但它不是一个迭代方法。它会就地修改数组，不接受 `thisArg`，并且可能在索引上多次调用回调函数。

### 通用数组方法

数组方法总是通用的——它们不访问数组对象的任何内部数据。它们只通过 `length` 属性和索引访问数组元素。这意味着它们也可以在类数组对象上调用。

```js
const arrayLike = {
  0: "a",
  1: "b",
  length: 2,
};
console.log(Array.prototype.join.call(arrayLike, "+")); // 'a+b'
```

#### 长度属性的规范化

`length` 属性被转换为一个数字，被截断为一个整数，然后固定为 0 到 253 - 1 之间的范围。`NaN` 变成 `0`，所以即使 `length` 没有出现或是 `undefined`，它也会表现得好像它的值是 `0`。

JavaScript 避免将 `length` 设置为不安全的整数。如果 `length` 将被设置为大于 253 - 1 的数字，则所有内置方法都将抛出 `TypeError`。但是，由于数组的 `length` 属性在设置为大于 232 时会抛出错误，因此通常不会达到安全整数阈值，除非该方法在非数组对象上调用。

```js
Array.prototype.flat.call({}); // []
```

一些数组方法会设置数组对象的 `length` 属性。它们总是在规范化后设置值，因此 `length` 总是以整数结尾。

```js
const a = { length: 0.7 };
Array.prototype.push.call(a);
console.log(a.length); // 0
```

#### 类数组对象

术语*类数组对象*指的是在上面描述的 `length` 转换过程中不抛出的任何对象。在实践中，这样的对象应该实际具有 `length` 属性，并且索引元素的范围在 `0` 到 `length - 1` 之间。（如果它没有所有的索引，它将在功能上等同于稀疏数组。）

许多 DOM 对象都是类数组对象——例如 `NodeList` 和 `HTMLCollection`。`arguments` 对象也是类数组对象。你可以在它们上调用数组方法，即使它们本身没有这些方法。

```js
function f() {
  console.log(Array.prototype.join.call(arguments, "+"));
}

f("a", "b"); // 'a+b'
```

构造函数
----

`Array()`

创建一个新的 `Array` 对象。

静态属性
----

`get Array[@@species]`

返回 `Array` 构造函数。

静态方法
----

* `Array.from()`
    + : 从数组类对象或可迭代对象创建一个新的 `Array` 实例。
* `Array.fromAsync()`
实验性

    + 从异步可迭代、可迭代或类数组对象创建新的 `Array` 实例。
* `Array.isArray()`
    + : 如果参数是数组则返回 `true` ，否则返回 `false` 。
* `Array.of()`
    + : 创建一个新的 `Array` 实例，具有可变数量的参数，而不管参数的数量或类型。
实例属性
----

以下属性在 `Array.prototype` 上定义，并由所有 `Array` 实例共享。

`Array.prototype.constructor`

创建实例对象的构造函数。对于 `Array` 实例，初始值是 `Array` 构造函数。

`Array.prototype[@@unscopables]`

包含 ES2015 版本之前 ECMAScript 标准中没有包含的属性名，在使用 `with` 绑定语句时会被忽略。

以下属性是每个 `Array` 实例自有的属性。

`Array.prototype.length`

反映数组中元素的数量。

实例方法
----

`Array.prototype.at()`

返回给定索引处的数组元素。接受从最后一项往回计算的负整数。

`Array.prototype.concat()`

返回一个新数组，该数组由被调用的数组与其他数组或值连接形成。

`Array.prototype.copyWithin()`

在数组内复制数组元素序列。

`Array.prototype.entries()`

返回一个新的*数组迭代器*对象，其中包含数组中每个索引的键/值对。

`Array.prototype.every()`

如果调用数组中的每个元素都满足测试函数，则返回 `true`。

`Array.prototype.fill()`

用静态值填充数组中从开始索引到结束索引的所有元素。

`Array.prototype.filter()`

返回一个新数组，其中包含调用所提供的筛选函数返回为 `true` 的所有数组元素。

`Array.prototype.find()`

返回数组中满足提供的测试函数的第一个元素的值，如果没有找到合适的元素，则返回 `undefined`。

`Array.prototype.findIndex()`

返回数组中满足提供的测试函数的第一个元素的索引，如果没有找到合适的元素，则返回 `-1`。

`Array.prototype.findLast()`

返回数组中满足提供的测试函数的最后一个元素的值，如果没有找到合适的元素，则返回 `undefined`。

`Array.prototype.findLastIndex()`

返回数组中满足所提供测试函数的最后一个元素的索引，如果没有找到合适的元素，则返回 `-1`。

`Array.prototype.flat()`

返回一个新数组，所有子数组元素递归地连接到其中，直到指定的深度。

`Array.prototype.flatMap()`

对调用数组的每个元素调用给定的回调函数，然后将结果展平一层，返回一个新数组。

`Array.prototype.forEach()`

对调用数组中的每个元素调用给定的函数。

`Array.prototype.includes()`

确定调用数组是否包含一个值，根据情况返回 `true` 或 `false`。

`Array.prototype.indexOf()`

返回在调用数组中可以找到给定元素的第一个（最小）索引。

`Array.prototype.join()`

将数组的所有元素连接为字符串。

`Array.prototype.keys()`

返回一个新的*数组迭代器*，其中包含调用数组中每个索引的键。

`Array.prototype.lastIndexOf()`

返回在调用数组中可以找到给定元素的最后一个（最大）索引，如果找不到则返回 `-1`。

`Array.prototype.map()`

返回一个新数组，其中包含对调用数组中的每个元素调用函数的结果。

`Array.prototype.pop()`

从数组中移除最后一个元素并返回该元素。

`Array.prototype.push()`

在数组末尾添加一个或多个元素，并返回数组新的 `length`。

`Array.prototype.reduce()`

对数组的每个元素（从左到右）执行用户提供的“reducer”回调函数，将其简化为单个值。

`Array.prototype.reduceRight()`

对数组的每个元素（从右到左）执行用户提供的“reducer”回调函数，将其简化为单个值。

`Array.prototype.reverse()`

*就地*反转数组中元素的顺序。（前面变成后面，后面变成前面。）

`Array.prototype.shift()`

从数组中移除第一个元素并返回该元素。

`Array.prototype.slice()`

提取调用数组的一部分并返回一个新数组。

`Array.prototype.some()`

如果调用数组中至少有一个元素满足提供的测试函数，则返回 `true`。

`Array.prototype.sort()`

对数组的元素进行排序并返回该数组。

`Array.prototype.splice()`

从数组中添加和/或删除元素。

`Array.prototype.toLocaleString()`

返回一个表示调用数组及其元素的本地化字符串。重写 `Object.prototype.toLocaleString()` 方法。

`Array.prototype.toReversed()`

返回一个新数组，该数组的元素顺序被反转，但不改变原始数组。

`Array.prototype.toSorted()`

返回一个新数组，其中元素按升序排序，而不改变原始数组。

`Array.prototype.toSpliced()`

返回一个新数组，在给定索引处删除和/或替换了一些元素，而不改变原始数组。

`Array.prototype.toString()`

返回一个表示调用数组及其元素的字符串。重写 `Object.prototype.toString()` 方法。

`Array.prototype.unshift()`

在数组的前面添加一个或多个元素，并返回数组新的 `length`。

`Array.prototype.values()`

返回一个新的*数组迭代器*对象，该对象包含数组中每个索引的值。

`Array.prototype.with()`

返回一个新数组，其中给定索引处的元素替换为给定值，而不改变原始数组。

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

默认情况下，该方法为 `values()` 方法的别名。

示例
--

本节提供一些 JavaScript 中常见的数组操作示例。

**备注：** 如果你还不熟悉数组的基础知识，可以考虑先读一下 JavaScript 第一步：数组，它解释了数组是什么，还包括其他常见的数组操作示例。

### 创建数组

下面的例子展示了三种创建新数组的方法：首先使用数组字面量，然后使用 `Array()` 构造函数，最后使用 `String.prototype.split()` 从字符串构建数组。

```js
// 使用数组字面量创建的 'fruits' 数组。
const fruits = ["Apple", "Banana"];
console.log(fruits.length);
// 2

// 使用 Array() 构建函数创建的 'fruits2' 数组。
const fruits2 = new Array("Apple", "Banana");
console.log(fruits2.length);
// 2

// 使用 String.prototype.split() 方法创建的 'fruits3' 数组。
const fruits3 = "Apple, Banana".split(", ");
console.log(fruits3.length);
// 2
```

### 从数组中创建一个字符串

下面的例子使用 `join()` 方法从 `fruits` 数组中创建一个字符串。

```js
const fruits = ["Apple", "Banana"];
const fruitsString = fruits.join(", ");
console.log(fruitsString);
// "Apple, Banana"
```

### 通过索引访问数组元素

下面的例子展示了如何通过指定它们在数组中的位置的索引号来访问 `fruits` 数组中的元素。

```js
const fruits = ["Apple", "Banana"];

// 数组第一个元素的索引始终为 0。
fruits[0]; // Apple

// 数组第二个元素的索引始终为 1。
fruits[1]; // Banana

// 数组最后一个元素的索引总是比数组的长度小 1。
fruits[fruits.length - 1]; // Banana

// 使用大于数组长度的索引会返回"undefined"
fruits[99]; // undefined
```

### 在数组中查找元素的索引

下面的例子使用 `indexOf()` 方法查找字符串 `"Banana"` 在 `fruits` 数组中的位置（索引）。

```js
const fruits = ["Apple", "Banana"];
console.log(fruits.indexOf("Banana"));
// 1
```

### 检查数组是否包含某个元素

下面的例子展示了两种检查 `fruits` 数组是否包含 `"Banana"` 和 `"Cherry"` 的方法：首先使用 `includes()` 方法，然后使用 `indexOf()` 方法来测试索引值不是 `-1`。

```js
const fruits = ["Apple", "Banana"];

fruits.includes("Banana"); // true
fruits.includes("Cherry"); // false

// 如果 indexOf() 不返回 -1，则数组包含给定的元素。
fruits.indexOf("Banana") !== -1; // true
fruits.indexOf("Cherry") !== -1; // false
```

### 将元素添加到数组中

下面的例子使用 `push()` 方法向 `fruits` 数组追加一个新字符串。

```js
const fruits = ["Apple", "Banana"];
const newLength = fruits.push("Orange");
console.log(fruits);
// ["Apple", "Banana", "Orange"]
console.log(newLength);
// 3
```

### 移除数组中的最后一个元素

下面的例子使用 `pop()` 方法从 `fruits` 数组中移除最后一个元素。

```js
const fruits = ["Apple", "Banana", "Orange"];
const removedItem = fruits.pop();
console.log(fruits);
// ["Apple", "Banana"]
console.log(removedItem);
// Orange
```

**备注：** `pop()` 只能用于从数组中移除最后一个元素。若要从数组末尾移除多个元素，请参见下一个示例。

### 从数组末尾移除多个元素

下面的例子使用 `splice()` 方法从 `fruits` 数组中移除最后 3 个元素。

```js
const fruits = ["Apple", "Banana", "Strawberry", "Mango", "Cherry"];
const start = -3;
const removedItems = fruits.splice(start);
console.log(fruits);
// ["Apple", "Banana"]
console.log(removedItems);
// ["Strawberry", "Mango", "Cherry"]
```

### 将数组截断为前 N 个元素

下面的例子使用 `splice()` 方法将 `fruits` 数组截断到只剩下前 2 个元素。

```js
const fruits = ["Apple", "Banana", "Strawberry", "Mango", "Cherry"];
const start = 2;
const removedItems = fruits.splice(start);
console.log(fruits);
// ["Apple", "Banana"]
console.log(removedItems);
// ["Strawberry", "Mango", "Cherry"]
```

### 移除数组中的第一个元素

下面的例子使用 `shift()` 方法从 `fruits` 数组中移除第一个元素。

```js
const fruits = ["Apple", "Banana"];
const removedItem = fruits.shift();
console.log(fruits);
// ["Banana"]
console.log(removedItem);
// Apple
```

**备注：** `shift()` 只能用于从数组中移除第一个元素。若要从数组的开头移除多个元素，请参见下一个示例。

### 从数组开头移除多个元素

下面的例子使用 `splice()` 方法从 `fruits` 数组中移除前 3 个元素。

```js
const fruits = ["Apple", "Strawberry", "Cherry", "Banana", "Mango"];
const start = 0;
const deleteCount = 3;
const removedItems = fruits.splice(start, deleteCount);
console.log(fruits);
// ["Banana", "Mango"]
console.log(removedItems);
// ["Apple", "Strawberry", "Cherry"]
```

### 向数组开头添加一个新的元素

下面的例子使用 `unshift()` 方法在索引 `0` 处向 `fruits` 数组中添加一个新元素——使其成为数组中新的第一元素。

```js
const fruits = ["Banana", "Mango"];
const newLength = fruits.unshift("Strawberry");
console.log(fruits);
// ["Strawberry", "Banana", "Mango"]
console.log(newLength);
// 3
```

### 按索引移除单个元素

下面的例子使用 `splice()` 方法从 `fruits` 数组中删除字符串 `"Banana"`——通过指定 `"Banana"` 的索引位置。

```js
const fruits = ["Strawberry", "Banana", "Mango"];
const start = fruits.indexOf("Banana");
const deleteCount = 1;
const removedItems = fruits.splice(start, deleteCount);
console.log(fruits);
// ["Strawberry", "Mango"]
console.log(removedItems);
// ["Banana"]
```

### 按索引移除多个元素

下面的例子使用 `splice()` 方法从 `fruits` 数组中删除字符串 `"Banana"` 和 `"Strawberry"`——通过指定 `"Banana"` 的索引位置，以及要移除的元素总数。

```js
const fruits = ["Apple", "Banana", "Strawberry", "Mango"];
const start = 1;
const deleteCount = 2;
const removedItems = fruits.splice(start, deleteCount);
console.log(fruits);
// ["Apple", "Mango"]
console.log(removedItems);
// ["Banana", "Strawberry"]
```

### 替换数组中的多个元素

下面的例子使用 `splice()` 方法将 `fruits` 数组中的最后两个元素替换为新元素。

```js
const fruits = ["Apple", "Banana", "Strawberry"];
const start = -2;
const deleteCount = 2;
const removedItems = fruits.splice(start, deleteCount, "Mango", "Cherry");
console.log(fruits);
// ["Apple", "Mango", "Cherry"]
console.log(removedItems);
// ["Banana", "Strawberry"]
```

### 遍历数组

下面的例子使用 `for...of` 循环遍历 `fruits` 数组，将每一个元素打印到控制台。

```js
const fruits = ["Apple", "Mango", "Cherry"];
for (const fruit of fruits) {
  console.log(fruit);
}
// Apple
// Mango
// Cherry
```

但 `for...of` 只是遍历任意数组的众多方法之一；更多方法，参见循环与迭代，并查看 `every()`、`filter()`、`flatMap()`、`map()`、`reduce()` 和 `reduceRight()` 方法——并参见下一个示例，该示例使用 `forEach()` 方法。

### 对数组中的每个元素调用函数

下面的例子使用 `forEach()` 方法在 `fruits` 数组中的每个元素上调用一个函数；该函数将每个元素以及元素的索引号打印到控制台。

```js
const fruits = ["Apple", "Mango", "Cherry"];
fruits.forEach((item, index, array) => {
  console.log(item, index);
});
// Apple 0
// Mango 1
// Cherry 2
```

### 合并多个数组

下面的例子使用 `concat()` 方法将 `fruits` 数组与 `moreFruits` 数组合并，生成一个新的 `combinedFruits` 数组。注意，`fruits` 和 `moreFruits` 保持不变。

```js
const fruits = ["Apple", "Banana", "Strawberry"];
const moreFruits = ["Mango", "Cherry"];
const combinedFruits = fruits.concat(moreFruits);
console.log(combinedFruits);
// ["Apple", "Banana", "Strawberry", "Mango", "Cherry"]

// The 'fruits' array remains unchanged.
console.log(fruits);
// ["Apple", "Banana", "Strawberry"]

// The 'moreFruits' array also remains unchanged.
console.log(moreFruits);
// ["Mango", "Cherry"]
```

### 复制数组

下面的例子展示了从现有的 `fruits` 数组创建新数组的三种方法：首先使用展开语法，然后使用 `from()` 方法，然后使用 `slice()` 方法。

```js
const fruits = ["Strawberry", "Mango"];

// Create a copy using spread syntax.
const fruitsCopy = [...fruits];
// ["Strawberry", "Mango"]

// Create a copy using the from() method.
const fruitsCopy2 = Array.from(fruits);
// ["Strawberry", "Mango"]

// Create a copy using the slice() method.
const fruitsCopy3 = fruits.slice();
// ["Strawberry", "Mango"]
```

所有内置的数组复制操作（展开语法、`Array.from()`、`Array.prototype.slice()` 和 `Array.prototype.concat()`）都会创建浅拷贝。如果你想要一个数组的深拷贝，你可以使用 `JSON.stringify()` 将数组转换成一个 JSON 字符串，然后使用 `JSON.parse()` 将字符串转换回一个完全独立于原数组的新数组。

```js
const fruitsDeepCopy = JSON.parse(JSON.stringify(fruits));
```

你还可以使用 `structuredClone()` 方法创建深拷贝，该方法的优点是允许源代码中的可转移对象被*转移*到新的副本，而不仅仅是克隆。

最后，重要的是要理解，将现有数组赋值给新变量并不会创建数组或其元素的副本。相反，新变量只是对原数组的引用或别名；也就是说，原来的数组名和新的变量名只是同一个对象的两个名称（因此总是被计算为严格相等）。因此，如果你对原数组的值或新变量的值做了任何改变，另一个也会改变：

```js
const fruits = ["Strawberry", "Mango"];
const fruitsAlias = fruits;
// 'fruits' 和 'fruitsAlias' 是同一个对象，严格相等。
fruits === fruitsAlias; // true
// 对 'fruits' 数组的任何更改也会更改 'fruitsAlias'。
fruits.unshift("Apple", "Banana");
console.log(fruits);
// ['Apple', 'Banana', 'Strawberry', 'Mango']
console.log(fruitsAlias);
// ['Apple', 'Banana', 'Strawberry', 'Mango']
```

### 创建二维数组

下面的例子创建了一个代表棋盘的二维字符串数组。第一步是将 `board[6][4]` 中的 `'p'` 复制到 `board[4][4]`。原本的 `[6][4]` 位置则被设置为空格。

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

console.log(`${board.join("\n")}\n\n`);

// 将国王的兵向前移动 2
board[4][4] = board[6][4];
board[6][4] = " ";
console.log(board.join("\n"));
```

下面是输出：
```

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

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

### 使用数组将一组值制成表格

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

结果为：
```

// The first column is the index
0  1    0
1  2    2
2  4    8
3  8    18
4  16   32
5  32   50
6  64   72
7  128  98
8  256  128
9  512  162
```

### 使用匹配的结果创建数组

`RegExp` 和字符串之间的匹配结果可以创建一个 JavaScript 数组，该数组具有匹配信息的属性和元素。这样的数组由 `RegExp.prototype.exec()` 和 `String.prototype.match()` 返回。

例如：

```js
// 匹配一个 d 后跟一个或多个 b 后跟一个 d
// 记住匹配的 b 和后面的 d
// 忽略大小写

const myRe = /d(b+)(d)/i;
const execResult = myRe.exec("cdbBdbsbz");

console.log(execResult.input); // 'cdbBdbsbz'
console.log(execResult.index); // 1
console.log(execResult); // [ "dbBd", "bB", "d" ]
```

有关匹配结果的更多信息，请参见 `RegExp.prototype.exec()` 和 `String.prototype.match()` 页。

规范
--

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

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

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

参见
--

* 索引集合类
* `TypedArray`
* `ArrayBuffer`


TypeError - JavaScript

TypeError
=========

**`TypeError`**（类型错误）对象通常（但并不只是）用来表示值的类型非预期类型时发生的错误。

以下情况会抛出 `TypeError`：

* 传递给运算符的操作数或传递给函数的参数与预期的类型不兼容；或
* 尝试修改无法更改的值；或
* 尝试以不适当的方法使用一个值。

`TypeError` 是一个可序列化对象，所以可以使用 `structuredClone()` 对它进行克隆，也可以使用 `postMessage()` 在 Worker 之间拷贝它。

构造函数
----

`TypeError()` (en-US)

创建一个新的 `TypeError` 对象。

实例属性
----

`Error.prototype.message`

错误消息。继承自 `Error`。

`Error.prototype.name`

错误名称。继承自 `Error`。

`Error.prototype.cause`

表示导致当前错误被抛出的原因。继承自 `Error`。

`Error.prototype.fileName`
非标准

引发此错误的文件的路径。继承自 `Error`。

`Error.prototype.lineNumber`
非标准

引发此错误的代码所在的文件的行号。继承自 `Error`。

`Error.prototype.columnNumber`
非标准

引发此错误的代码在文件中所在行的列号。继承自 `Error`。

`Error.prototype.stack`
非标准

堆栈跟踪。继承自 `Error`。

示例
--

### 捕获类型错误

```js
try {
  null.f();
} catch (e) {
  console.log(e instanceof TypeError); // true
  console.log(e.message); // "null has no properties"
  console.log(e.name); // "TypeError"
  console.log(e.fileName); // "Scratchpad/1"
  console.log(e.lineNumber); // 2
  console.log(e.columnNumber); // 2
  console.log(e.stack); // "@Scratchpad/2:2:3\n"
}
```

### 创建一个类型错误

```js
try {
  throw new TypeError("Hello", "someFile.js", 10);
} catch (e) {
  console.log(e instanceof TypeError); // true
  console.log(e.message); // "Hello"
  console.log(e.name); // "TypeError"
  console.log(e.fileName); // "someFile.js"
  console.log(e.lineNumber); // 10
  console.log(e.columnNumber); // 0
  console.log(e.stack); // "@Scratchpad/2:2:9\n"
}
```

规范
--

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

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

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

参见
--

* `Error`


Function - JavaScript

Function
========

**`Function`** 对象提供了用于处理函数的方法。在 JavaScript 中，每个函数实际上都是一个 `Function` 对象。

构造函数
----

`Function()`

创建一个新的 `Function` 对象。直接调用此构造函数可以动态创建函数，但会遇到和 `eval()` 类似的的安全问题和（相对较小的）性能问题。然而，与 `eval()` 不同的是，`Function` 构造函数创建的函数只能在全局作用域中运行。

实例属性
----

以下属性定义在 `Function.prototype` 上，并且被所有 `Function` 实例共享。

`Function.prototype.arguments`
已弃用

非标准

表示传递给该函数的参数。对于严格模式、箭头函数、异步函数和生成器函数，访问 `arguments` 属性会抛出 `TypeError` 异常。请改为在函数闭包内使用 `arguments` 对象。

`Function.prototype.caller`
已弃用

非标准

表示调用该函数的函数。对于严格模式、箭头函数、异步函数和生成器函数，访问 `caller` 属性会抛出 `TypeError` 异常。

`Function.prototype.constructor`

创建实例对象的构造函数。对于 `Function` 实例来说，初始值是 `Function` 构造函数。

以下属性是每个 `Function` 实例的自有属性。

`displayName`
非标准
 可选

函数的显示名称。

`length`

指定函数期望的参数个数。

`name`

函数的名称。

`prototype`

在使用 `function` 作为构造函数与 `new` 运算符一起使用时，用作新对象的原型。

实例方法
----

`Function.prototype.apply()`

使用给定的 `this` 值和可选的参数数组（或类数组对象）作为参数来调用一个函数。

`Function.prototype.bind()`

创建一个新的函数，在调用时，其 `this` 关键字被设置为提供的值，可选地在调用新函数时在提供的参数之前加上一系列给定的参数。

`Function.prototype.call()`

使用给定的 `this` 值和可选参数调用一个函数。

`Function.prototype.toString()`

返回表示函数源代码的字符串。重写了 `Object.prototype.toString` 方法。

`Function.prototype[@@hasInstance]()`

指定确定构造函数是否将对象识别为其实例的默认过程。由 `instanceof` 运算符调用。

示例
--

### Function 构造函数与函数声明之间的不同

由 `Function` 构造函数创建的函数不会创建当前环境的闭包，它们总是被创建于全局环境，因此在运行时它们只能访问全局变量和自己的局部变量，不能访问它们被 `Function` 构造函数创建时所在的作用域的变量。这一点与使用 `eval()` 执行创建函数的代码不同。

```js
// 使用 `var` 创建一个全局属性
var x = 10;

function createFunction1() {
  const x = 20;
  return new Function("return x;"); // 这个 `x` 指的是全局 `x`
}

function createFunction2() {
  const x = 20;
  function f() {
    return x; // 这个 `x` 指的是上面的局部 `x`
  }
  return f;
}

const f1 = createFunction1();
console.log(f1()); // 10
const f2 = createFunction2();
console.log(f2()); // 20
```

虽然这段代码可以在浏览器中正常运行，但在 Node.js 中 `f1()` 会产生一个“找不到变量 `x`”的 `ReferenceError`。这是因为在 Node 中顶级作用域不是全局作用域，而 `x` 其实是在当前模块的作用域之中。

规范
--

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

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

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

参见
--

* 函数声明
* 函数表达式
* `AsyncFunction`
* `AsyncGeneratorFunction`
* `GeneratorFunction`
* 函数


JSON - JavaScript

JSON
====

**`JSON`** 对象包含两个方法：用于解析 JavaScript Object Notation（JSON）的 `parse()` 方法，以及将对象/值转换为 JSON 字符串的 `stringify()` 方法。除了这两个方法，JSON 这个对象本身并没有其他作用，也不能被调用或者作为构造函数调用。

描述
--

### JavaScript Object Notation

**JSON** 是一种语法，用来序列化对象、数组、数值、字符串、布尔值和 `null` 。它基于 JavaScript 语法，但与之不同：**JavaScript 不是 JSON，JSON 也不是 JavaScript**。参考 JSON：并不是 JavaScript 的子集。

JavaScript 与 JSON 的区别| JavaScript 类型 | JSON 的不同点 |
| --- | --- |
| 对象和数组 | 属性名称必须是双引号括起来的字符串；最后一个属性后不能有逗号。 |
| 数值 | 禁止出现前导零（JSON.stringify 方法自动忽略前导零，而在 JSON.parse 方法中将会抛出 SyntaxError）；如果有小数点，则后面至少跟着一位数字。 |
| 字符串 | 只有有限的一些字符可能会被转义；禁止某些控制字符；Unicode 行分隔符（U+2028）和段分隔符（U+2029）被允许 ; 字符串必须用双引号括起来。请参考下面的示例，可以看到 `JSON.parse()` 能够正常解析，但将其当作 JavaScript 解析时会抛出 `SyntaxError` 错误：

```js
let code = '"\u2028\u2029"';
JSON.parse(code); // 正常
eval(code); // 错误
```

 |

完整的 JSON 语法定义如下：
```

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

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

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

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

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

在 `JSONNumber`（数字内部不允许包含空格）或 `JSONString`（字符串内部的空格被解释为相应的字符，否则就有问题了）之外的任何位置可以有多余的空白字符。JSON 只支持这些空白字符：制表符（U+0009），回车（U+000D），换行（U+000A）以及空格（U+0020）。

方法
--

`JSON.parse()`

解析 JSON 字符串并返回对应的值，可以额外传入一个转换函数，用来将生成的值和其属性，在返回之前进行某些修改。

`JSON.stringify()`

返回与指定值对应的 JSON 字符串，可以通过额外的参数，控制仅包含某些属性，或者以自定义方法来替换某些 key 对应的属性值。

规范
--

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

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

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

参见
--

* `Date.prototype.toJSON()`


DataView - JavaScript

DataView
========

**`DataView`** 视图是一个可以从二进制 `ArrayBuffer` 对象中读写多种数值类型的底层接口，使用它时，不用考虑不同平台的字节序（endianness）问题。

描述
--

### 字节序

多字节的数字格式在内存中的表示方式因机器架构而异——相关的解释请参阅字节序。`DataView` 访问器（accessor）提供了对如何访问数据的明确控制，而不管执行代码的计算机的字节序如何。

```js
const littleEndian = (() => {
  const buffer = new ArrayBuffer(2);
  new DataView(buffer).setInt16(0, 256, true /\* 小端对齐 \*/);
  // Int16Array 使用平台的字节序。
  return new Int16Array(buffer)[0] === 256;
})();
console.log(littleEndian); // true 或 false
```

### 64 位整数值

某些浏览器不支持 `DataView.prototype.setBigInt64()` 和 `DataView.prototype.setBigUint64()`。因此，要在代码中启用跨浏览器的 64 位操作，你可以实现自己的 `getUint64()` 函数，以获得精度达到 `Number.MAX_SAFE_INTEGER` 的值——这对于某些情况足够使用。

```js
function getUint64(dataview, byteOffset, littleEndian) {
  // 将 64 位的数字拆分位两个 32 位（4 字节）的部分
  const left = dataview.getUint32(byteOffset, littleEndian);
  const right = dataview.getUint32(byteOffset + 4, littleEndian);

  // 将两个 32 位的值组合在一起
  const combined = littleEndian
    ? left + 2 \*\* 32 \* right
    : 2 \*\* 32 \* left + right;

  if (!Number.isSafeInteger(combined))
    console.warn(combined, "超过 MAX\_SAFE\_INTEGER。可能存在精度丢失。");

  return combined;
}
```

或者，如果你需要完整的 64 位的范围，你可以创建 `BigInt`。此外，尽管原生 BigInt 比等效的用户态的库快得多，但由于其大小可变的性质，BigInt 始终比 JavaScript 中的 32 位整数要慢得多。

```js
const BigInt = window.BigInt,
  bigThirtyTwo = BigInt(32),
  bigZero = BigInt(0);
function getUint64BigInt(dataview, byteOffset, littleEndian) {
  // 将 64 位的数字拆分位两个 32 位（4 字节）的部分
  const left = BigInt(dataview.getUint32(byteOffset | 0, !!littleEndian) >>> 0);
  const right = BigInt(
    dataview.getUint32(((byteOffset | 0) + 4) | 0, !!littleEndian) >>> 0,
  );

  // 将两个 32 位的值组合在一起并返回该值
  return littleEndian
    ? (right << bigThirtyTwo) | left
    : (left << bigThirtyTwo) | right;
}
```

构造函数
----

`DataView()` (en-US)

创建一个新的 `DataView` 对象。

实例属性
----

`DataView.prototype[@@toStringTag]`

`@@toStringTag` 属性的初始值为字符串 `"DataView"`。该属性用于 `Object.prototype.toString()`。

`DataView.prototype.buffer`

`ArrayBuffer` 是引用该缓冲区的视图。在构造时会被固定，因此该属性**只读**。

`DataView.prototype.byteLength`

视图的长度（以字节为单位）。在构造时会被固定，因此该属性**只读**。

`DataView.prototype.byteOffset`

至 `ArrayBuffer` 的视图开始位置的字节偏移量。在构造时会被固定，因此该属性**只读**。

实例方法
----

`DataView.prototype.getInt8()`

从视图开始的指定字节偏移处获取一个带符号 8 位整数（byte）。

`DataView.prototype.getUint8()`

从视图开始的指定字节偏移处获取一个无符号 8 位整数（unsigned byte）。

`DataView.prototype.getInt16()`

从视图开始的指定字节偏移处获取一个带符号 16 位整数（short）。

`DataView.prototype.getUint16()`

从视图开始的指定字节偏移处获取一个无符号 16 位整数（unsigned short）。

`DataView.prototype.getInt32()`

从视图开始的指定字节偏移处获取一个带符号 32 位整数（long）。

`DataView.prototype.getUint32()`

从视图开始的指定字节偏移处获取一个无符号 32 位整数（unsigned long）。

`DataView.prototype.getFloat32()`

从视图开始的指定字节偏移处获取一个带符号 32 位浮点数（float）。

`DataView.prototype.getFloat64()`

从视图开始的指定字节偏移处获取一个带符号 64 位浮点数（double）。

`DataView.prototype.getBigInt64()`

从视图开始的指定字节偏移处获取一个带符号 64 位整数（long long）。

`DataView.prototype.getBigUint64()`

从视图开始的指定字节偏移处获取一个无符号 64 位整数（unsigned long long）。

`DataView.prototype.setInt8()`

在视图开始的指定字节偏移处存储一个带符号 8 位整数（byte）。

`DataView.prototype.setUint8()`

在视图开始的指定字节偏移处存储一个无符号 8 位整数（unsigned byte）。

`DataView.prototype.setInt16()`

在视图开始的指定字节偏移处存储一个带符号 16 位整数（short）。

`DataView.prototype.setUint16()`

在视图开始的指定字节偏移处存储一个无符号 16 位整数（unsigned short）。

`DataView.prototype.setInt32()`

在视图开始的指定字节偏移处存储一个带符号 32 位整数（long）。

`DataView.prototype.setUint32()`

在视图开始的指定字节偏移处存储一个无符号 32 位整数（unsigned long）。

`DataView.prototype.setFloat32()`

在视图开始的指定字节偏移处存储一个带符号 32 位浮点数（float）。

`DataView.prototype.setFloat64()`

在视图开始的指定字节偏移处存储一个带符号 64 位浮点数（double）。

`DataView.prototype.setBigInt64()`

在视图开始的指定字节偏移处存储一个带符号 64 位 BigInt（long long）。

`DataView.prototype.setBigUint64()`

在视图开始的指定字节偏移处存储一个无符号 64 位 BigInt（unsigned long long）。

示例
--

### 使用 DataView

```js
const buffer = new ArrayBuffer(16);
const view = new DataView(buffer, 0);

view.setInt16(1, 42);
view.getInt16(1); // 42
```

规范
--

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

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

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

参见
--

* `DataView` 在 `core-js` 中的 polyfill
* jDataView：DataView 的 polyfill，使其能够用于所有浏览器及 Node.js 环境。同时，该库还对 `DataView` API 进行了一些扩展。
* `ArrayBuffer`
* `SharedArrayBuffer`


ReferenceError - JavaScript

ReferenceError
==============

**`ReferenceError`**（引用错误）对象代表当一个不存在（或尚未初始化）的变量被引用时发生的错误。

`ReferenceError` 是一个可序列化对象，所以可以使用 `structuredClone()` 对它进行克隆，也可以使用 `postMessage()` 在 Worker 之间拷贝它。

构造函数
----

`ReferenceError()` (en-US)

创建一个新的 `ReferenceError` 对象。

实例属性
----

`Error.prototype.message`

错误消息。继承自 `Error`。

`Error.prototype.name`

错误名称。继承自 `Error`。

`Error.prototype.cause`

表示导致当前错误被抛出的原因。继承自 `Error`。

`Error.prototype.fileName`
非标准

引发此错误的文件的路径。继承自 `Error`。

`Error.prototype.lineNumber`
非标准

引发此错误的代码所在的文件的行号。继承自 `Error`。

`Error.prototype.columnNumber`
非标准

引发此错误的代码在文件中所在行的列号。继承自 `Error`。

`Error.prototype.stack`
非标准

堆栈跟踪。继承自 `Error`。

示例
--

### 捕获一个 ReferenceError

```js
try {
  let a = undefinedVariable;
} catch (e) {
  console.log(e instanceof ReferenceError); // true
  console.log(e.message); // "undefinedVariable is not defined"
  console.log(e.name); // "ReferenceError"
  console.log(e.fileName); // "Scratchpad/1"
  console.log(e.lineNumber); // 2
  console.log(e.columnNumber); // 6
  console.log(e.stack); // "@Scratchpad/2:2:7\n"
}
```

### 创建一个 ReferenceError

```js
try {
  throw new ReferenceError("Hello", "someFile.js", 10);
} catch (e) {
  console.log(e instanceof ReferenceError); // true
  console.log(e.message); // "Hello"
  console.log(e.name); // "ReferenceError"
  console.log(e.fileName); // "someFile.js"
  console.log(e.lineNumber); // 10
  console.log(e.columnNumber); // 0
  console.log(e.stack); // "@Scratchpad/2:2:9\n"
}
```

规范
--

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

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

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

参见
--

* `Error`


Promise - JavaScript

Promise
=======

**`Promise`** 对象表示异步操作最终的完成（或失败）以及其结果值。

若想了解 promise 的工作方式以及如何使用它们，我们建议你先阅读使用 Promise。

描述
--

一个 **`Promise`** 是一个代理，它代表一个在创建 promise 时不一定已知的值。它允许你将处理程序与异步操作的最终成功值或失败原因关联起来。这使得异步方法可以像同步方法一样返回值：异步方法不会立即返回最终值，而是返回一个 *promise*，以便在将来的某个时间点提供该值。

一个 `Promise` 必然处于以下几种状态之一：

* *待定（pending）*：初始状态，既没有被兑现，也没有被拒绝。
* *已兑现（fulfilled）*：意味着操作成功完成。
* *已拒绝（rejected）*：意味着操作失败。

一个待定的 Promise *最终状态*可以是*已兑现*并返回一个值，或者是*已拒绝*并返回一个原因（错误）。当其中任意一种情况发生时，通过 Promise 的 `then` 方法串联的处理程序将被调用。如果绑定相应处理程序时 Promise 已经兑现或拒绝，这处理程序将被立即调用，因此在异步操作完成和绑定处理程序之间不存在竞态条件。

如果一个 Promise 已经被兑现或拒绝，即不再处于待定状态，那么则称之为已*敲定（settled）*。

![流程图展示了 Promise 状态在 pending、fulfilled 和 rejected 之间如何通过 then() 和 catch() 处理程序进行转换。一个待定的 Promise 可以变成已兑现或已拒绝的状态。如果 Promise 已经兑现，则会执行“on fulfillment”处理程序（即 then() 方法的第一个参数），并继续执行进一步的异步操作。如果 Promise 被拒绝，则会执行错误处理程序，可以将其作为 then() 方法的第二个参数或 catch() 方法的唯一参数来传递。](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/promises.png)

你还会听到使用*已解决*（resolved）这个术语来描述 Promise——这意味着该 Promise 已经敲定（settled），或为了匹配另一个 Promise 的最终状态而被“锁定（lock-in）”，进一步解决或拒绝它都没有影响。原始 Promise 提案中的 States and fates 文档包含了更多关于 Promise 术语的细节。在口语中，“已解决”的 Promise 通常等价于“已兑现”的 Promise，但是正如“States and fates”所示，已解决的 Promise 也可以是待定或拒绝的。例如：

```js
new Promise((resolveOuter) => {
  resolveOuter(
    new Promise((resolveInner) => {
      setTimeout(resolveInner, 1000);
    }),
  );
});
```

此 Promise 在创建时已经被解决（因为 `resolveOuter` 是同步调用的），但它是用另一个 Promise 解决的，因此在内部 Promise 兑现的 1 秒之后才会*被兑现*。在实践中，“解决”过程通常是在幕后完成的，不可观察，只有其兑现或拒绝是可观察的。

**备注：** 其他几种语言也有一些机制来实现惰性求值和延迟计算，它们也称之为“promise”，例如 Scheme。在 JavaScript 中，Promise 代表已经在进行中的进程，而且可以通过回调函数实现链式调用。如果你想要实现惰性求值，考虑使用不带参数的函数，例如 `f = () => expression` 来创建惰性求值表达式，然后使用 `f()` 立即求值。

### Promise 的链式调用

`Promise.prototype.then()`、`Promise.prototype.catch()` 和 `Promise.prototype.finally()` 方法用于将进一步的操作与已敲定的 Promise 相关联。由于这些方法返回 Promise，因此它们可以被链式调用。

`.then()` 方法最多接受两个参数；第一个参数是 Promise 兑现时的回调函数，第二个参数是 Promise 拒绝时的回调函数。每个 `.then()` 返回一个新生成的 Promise 对象，这个对象可被用于链式调用，例如：

```js
const myPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("foo");
  }, 300);
});

myPromise
  .then(handleFulfilledA, handleRejectedA)
  .then(handleFulfilledB, handleRejectedB)
  .then(handleFulfilledC, handleRejectedC);
```

即使 `.then()` 缺少返回 Promise 对象的回调函数，处理程序仍会继续到链的下一个链式调用。因此，在最终的 `.catch()` 之前，可以安全地省略每个链式调用中处理*已拒绝*状态的回调函数。

在每个 `.then()` 中处理被拒绝的 Promise 对于 Promise 链的下游有重要的影响。有时候别无选择，因为有的错误必须立即被处理。在这种情况下，必须抛出某种类型的错误以维护链中的错误状态。另一方面，在没有迫切需要的情况下，最好将错误处理留到最后一个 `.catch()` 语句。`.catch()` 其实就是一个没有为 Promise 兑现时的回调函数留出空位的 `.then()`。

```js
myPromise
  .then(handleFulfilledA)
  .then(handleFulfilledB)
  .then(handleFulfilledC)
  .catch(handleRejectedAny);
```

使用箭头函数作为回调函数，实现 Promise 的链式调用的示例如下：

```js
myPromise
  .then((value) => `${value} and bar`)
  .then((value) => `${value} and bar again`)
  .then((value) => `${value} and again`)
  .then((value) => `${value} and again`)
  .then((value) => {
    console.log(value);
  })
  .catch((err) => {
    console.error(err);
  });
```

**备注：** 为了更快的执行，最好将所有同步操作都放在一个处理程序中，否则如果将它们拆分为多个处理程序，执行所有处理程序将需要几个时钟周期。

一个 Promise 的终止条件决定了链中下一个 Promise 的“已敲定”状态。“已兑现”状态表示 Promise 成功完成，而“已拒绝”状态表示 Promise 执行失败。链中每个已兑现的 Promise 的返回值会传递给下一个 `.then()`，而已拒绝的 Promise 会把失败原因传递给链中下一个拒绝处理函数。

链式调用中的 promise 们就像俄罗斯套娃一样，是嵌套起来的，但又像是一个栈，每个都必须从顶端被弹出。链式调用中的第一个 promise 是嵌套最深的一个，也将是第一个被弹出的。
```

(promise D, (promise C, (promise B, (promise A) ) ) )
```

当存在一个 `nextValue` 是 promise 时，就会出现一种动态的替换效果。`return` 会导致一个 promise 被弹出，但这个 `nextValue` promise 则会被推入被弹出 promise 原来的位置。对于上面所示的嵌套场景，假设与“promise B”相关的 `.then()` 返回了一个值为“promise X”的 `nextValue` 。那么嵌套的结果看起来就会是这样：
```

(promise D, (promise C, (promise X) ) )
```

一个 promise 可能会参与不止一次的嵌套。对于下面的代码，`promiseA` 向“已敲定”状态的过渡会导致两个实例的 `.then()` 都被调用。

```js
const promiseA = new Promise(myExecutorFunc);
const promiseB = promiseA.then(handleFulfilled1, handleRejected1);
const promiseC = promiseA.then(handleFulfilled2, handleRejected2);
```

一个已经处于“已敲定”状态的 promise 也可以接收操作。在那种情况下，（如果没有问题的话）这个操作会被作为第一个异步操作被执行。注意，所有的 promise 都一定是异步的。因此，一个已经处于“已敲定”状态的 promise 中的操作只有 promise 链式调用的栈被清空且一个时间片段过去之后才会被执行。这种效果跟 `setTimeout(action, 10)` 特别相似。

```js
const promiseA = new Promise((resolve, reject) => {
  resolve(777);
});
// 此时，“promiseA”已经敲定了
promiseA.then((val) => console.log("异步日志记录有值：", val));
console.log("立即记录");

// 按以下顺序产生输出：
// 立即记录
// 异步日志记录有值：777
```

### Thenable

在 Promise 成为 JavaScript 语言的一部分之前，JavaScript 生态系统已经有了多种 Promise 实现。尽管它们在内部的表示方式不同，但至少所有类 Promise 的对象都实现了 *Thenable* 接口。thenable 对象实现了 `.then()` 方法，该方法被调用时需要传入两个回调函数，一个用于 Promise 被兑现时调用，一个用于 Promise 被拒绝时调用。Promise 也是 thenable 对象。

为了与现有的 Promise 实现进行交互，JavaScript 语言允许在 Promise 的位置使用 thenable 对象。例如，`Promise.resolve` 方法不仅可以解析 Promise 对象，还可以追踪 thenable 对象。

```js
const aThenable = {
  then(onFulfilled, onRejected) {
    onFulfilled({
      // thenable 对象被兑现为另一个 thenable 对象
      then(onFulfilled, onRejected) {
        onFulfilled(42);
      },
    });
  },
};

Promise.resolve(aThenable); // 一个兑现值为 42 的 Promise
```

### Promise 并发

`Promise` 类提供了四个静态方法来促进异步任务的并发：

`Promise.all()`

在**所有**传入的 Promise 都被兑现时兑现；在**任意一个** Promise 被拒绝时拒绝。

`Promise.allSettled()`

在**所有**的 Promise 都被敲定时兑现。

`Promise.any()`

在**任意一个** Promise 被兑现时兑现；仅在**所有**的 Promise 都被拒绝时才会拒绝。

`Promise.race()`

在**任意一个** Promise 被敲定时敲定。换句话说，在**任意一个** Promise 被兑现时兑现；在**任意一个**的 Promise 被拒绝时拒绝。

所有这些方法都接受一个 Promise（确切地说是 thenable）的可迭代对象，并返回一个新的 Promise。它们都支持子类化，这意味着它们可以在 `Promise` 的子类上调用，结果将是一个属于子类类型的 Promise。为此，子类的构造函数必须实现与 `Promise()` 构造函数相同的签名——接受一个以 `resolve` 和 `reject` 回调函数作为参数的单个 `executor` 函数。子类还必须有一个 `resolve` 静态方法，可以像 `Promise.resolve()` 一样调用，以将值解析为 Promise。

请注意，JavaScript 的本质上是单线程的，因此在任何时刻，只有一个任务会被执行，尽管控制权可以在不同的 Promise 之间切换，从而使 Promise 的执行看起来是并发的。在 JavaScript 中，并行执行只能通过 worker 线程实现。

构造函数
----

`Promise()`

创建一个新的 `Promise` 对象。该构造函数主要用于封装还没有添加 promise 支持的函数。

静态属性
----

`Promise[@@species]`

返回用于构造从 Promise 方法返回值的构造函数。

静态方法
----

`Promise.all()`

接受一个 Promise 可迭代对象作为输入，并返回单个 `Promise`。返回的 Promise 在所有输入的 Promise 都兑现时（包括传入的可迭代对象为空时）被兑现，其值为一个包含所有兑现值的数组。如果输入的任何 Promise 被拒绝，返回的 Promise 也会被拒绝，并返回第一个拒绝的原因。

`Promise.allSettled()`

接受一个 Promise 可迭代对象作为输入，并返回单个 `Promise`。返回的 Promise 在所有输入的 Promise 都敲定时兑现（包括传入的可迭代对象为空时），其值为一个描述每个 Promise 结果的对象数组。

`Promise.any()`

接受一个 Promise 可迭代对象作为输入，并返回单个 `Promise`。返回的 Promise 在任何输入的 Promise 兑现时兑现，其值为第一个兑现的值。如果所有输入的 Promise 都被拒绝（包括传入的可迭代对象为空时），返回的 Promise 将以带有一个包含拒绝原因的数组的 `AggregateError` 拒绝。

`Promise.race()`

接受一个 Promise 可迭代对象作为输入，并返回单个 `Promise`。返回的 Promise 与第一个敲定的 Promise 的最终状态保持一致。

`Promise.reject()`

返回一个新的 `Promise` 对象，该对象以给定的原因拒绝。

`Promise.resolve()`

返回一个新的 `Promise` 对象，该对象以给定的值兑现。如果值是一个 thenable 对象（即具有 `then` 方法），则返回的 Promise 对象会“跟随”该 thenable 对象，采用其最终的状态；否则，返回的 Promise 对象会以该值兑现。

通常，如果你不知道一个值是否是 Promise，那么最好使用 `Promise.resolve(value)` 将其转换成 Promise 对象，并将返回值作为 Promise 来处理。

实例属性
----

这些属性定义在 `Promise.prototype` 上，由所有的 `Promise` 实例共享。

`Promise.prototype.constructor`

创建实例对象的构造函数。对于 `Promise` 实例，初始值为 `Promise` 构造函数。

`Promise.prototype[@@toStringTag]`

`@@toStringTag` 属性的初始值为字符串 `"Promise"`。该属性用于 `Object.prototype.toString()`。

实例方法
----

`Promise.prototype.catch()`

将一个拒绝处理回调函数附加到 Promise 上，并返回一个新的 Promise，如果回调被调用，则解决为回调的返回值，如果 Promise 被兑现，解决为其原始兑现值。

`Promise.prototype.finally()`

将一个处理器附加到 Promise 上，并返回一个新的 Promise，当原始 Promise 被解决时解决。无论 Promise 是否被兑现还是被拒绝，处理器都会在 Promise 敲定时被调用。

`Promise.prototype.then()`

将一个兑现处理器和拒绝处理器附加到 Promise 上，并返回一个新的 Promise，解决为调用处理器得到的返回值，或者如果 Promise 没有被处理（即相关处理器 `onFulfilled` 或 `onRejected` 不是函数），则以原始敲定值解决。

示例
--

### 基础示例

```js
const myFirstPromise = new Promise((resolve, reject) => {
  // 当异步操作成功时，我们调用 resolve(...)，当其失败时，调用 reject(...)。
  // 在这个例子中，我们使用 setTimeout(...) 来模拟异步代码。
  // 在实际情况中，你可能会使用类似 XHR 或 HTML API 等。
  setTimeout(() => {
    resolve("成功！"); // 耶！一切顺利！
  }, 250);
});

myFirstPromise.then((successMessage) => {
  // successMessage 是我们在上面的 resolve(...) 函数中传入的任何内容。
  // 它不一定是字符串，但如果它只是一个成功的消息，那么它大概率是字符串。
  console.log(`耶！${successMessage}`);
});
```

### 不同场景的示例

此示例展示了使用 promise 的多种方法，以及其可能发生的多种情况。要理解这一点，首先滚动到代码块的底部，然后查看 promise 调用链。在创建初始的 promise 后，可以接上一条 promise 调用链。该调用链由 `.then()` 组成，通常（但不一定）在末尾会有一个 `.catch()`，并可能会接上一个 `.finally()`。在本示例中，promise 调用链是由一个自定义的 `new Promise()` 构造并发起的；但在实践中，promise 调用链通常由一个 API 函数（由其他人编写的）返回的 promise 开始。

示例函数 `tetheredGetNumber()` 会在设置同步调用或者函数内部抛出异常时调用 `reject()`。函数 `promiseGetWord()` 展示了如何在 API 函数内部创建并返回一个 promise。

请注意，函数 `troubleWithGetNumber()` 以 `throw()` 结束。这是强制的做法，因为 ES6 的 promise 会遍历所有的 `.then()` promise，在遇到错误时，如果不使用 `throw()`，这个错误会被当作“已修复”。这很麻烦，因此，通常会在 `.then()` promise 调用链中忽略 `rejectionFunc`，而仅在最后的 `.catch()` 中保留一个 `rejectionFunc`。另一种方法是抛出一个特殊值（本例使用了 `-999`，但使用自定义错误类型更合适）。

此代码可在 NodeJS 下运行。通过看到错误的实际发生，可以加深理解。若要提高错误发生的概率，请更改 `threshold` 值。

```js
// 为了尝试错误处理，使用“阈值”值会随机地引发错误。
const THRESHOLD\_A = 8; // 可以使用 0 使错误必现

function tetheredGetNumber(resolve, reject) {
  setTimeout(() => {
    const randomInt = Date.now();
    const value = randomInt % 10;
    if (value < THRESHOLD\_A) {
      resolve(value);
    } else {
      reject(`太大了：${value}`);
    }
  }, 500);
}

function determineParity(value) {
  const isOdd = value % 2 === 1;
  return { value, isOdd };
}

function troubleWithGetNumber(reason) {
  const err = new Error("获取数据时遇到问题", { cause: reason });
  console.error(err);
  throw err;
}

function promiseGetWord(parityInfo) {
  return new Promise((resolve, reject) => {
    const { value, isOdd } = parityInfo;
    if (value >= THRESHOLD\_A - 1) {
      reject(`还是太大了：${value}`);
    } else {
      parityInfo.wordEvenOdd = isOdd ? "奇数" : "偶数";
      resolve(parityInfo);
    }
  });
}

new Promise(tetheredGetNumber)
  .then(determineParity, troubleWithGetNumber)
  .then(promiseGetWord)
  .then((info) => {
    console.log(`得到了：${info.value}, ${info.wordEvenOdd}`);
    return info;
  })
  .catch((reason) => {
    if (reason.cause) {
      console.error("已经在前面处理过错误了");
    } else {
      console.error(`运行 promiseGetWord() 时遇到问题：${reason}`);
    }
  })
  .finally((info) => console.log("所有回调都完成了"));
```

### 高级示例

本例展示了 `Promise` 的一些机制。`testPromise()` 方法在每次点击 `<button>` 按钮时被调用，该方法会创建一个 promise 对象，使用 `setTimeout()` 让 `Promise` 等待 1-3 秒不等的时间来兑现计数结果（从 1 开始的数字）。使用 `Promise` 构造函数来创建 promise。

通过使用 `p1.then()` 设置兑现回调函数，并在其中记录 Promise 的兑现，这些日志显示了方法的同步代码是如何与 Promise 的异步完成是如何解耦的。

通过在短时间内多次点击按钮，你可以看到不同的 promise 被一个接一个地兑现。

#### HTML

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

#### JavaScript

```js
"use strict";

let promiseCount = 0;

function testPromise() {
  const thisPromiseCount = ++promiseCount;
  const log = document.getElementById("log");
  // 开始
  log.insertAdjacentHTML("beforeend", `${thisPromiseCount}) Started<br>`);
  // 我们创建一个新的 Promise：我们承诺在等待 3 秒后，兑现从 1 开始计数的数字
  const p1 = new Promise((resolve, reject) => {
    // 执行器函数被调用，并具有解决或拒绝该 Promise 的能力
    log.insertAdjacentHTML(
      "beforeend",
      `${thisPromiseCount}) Promise constructor<br>`,
    );
    // 这只是一个创建异步操作的示例
    setTimeout(
      () => {
        // We fulfill the promise
        resolve(thisPromiseCount);
      },
      Math.random() \* 2000 + 1000,
    );
  });

  // 我们使用 then() 来定义 Promise 被解决时的操作，
  // 并使用 catch() 来定义 Promise 被拒绝时的操作
  p1.then((val) => {
    // 打印兑现值
    log.insertAdjacentHTML("beforeend", `${val}) Promise fulfilled<br>`);
  }).catch((reason) => {
    // 打印拒绝原因
    console.log(`Handle rejected promise (${reason}) here.`);
  });
  // 结束
  log.insertAdjacentHTML("beforeend", `${thisPromiseCount}) Promise made<br>`);
}

const btn = document.getElementById("make-promise");
btn.addEventListener("click", testPromise);
```

#### 结果

### 使用 XHR 加载图像

另一个使用 `Promise` 和 `XMLHttpRequest` 加载一个图像的例子可在 MDN GitHub js-examples 仓库中找到。你也可以看它的实例。每一步都有注释可以让你详细的了解 Promise 和 XHR 架构。

### 追踪现有设置对象

设置对象（settings object）是 JavaScript 代码运行时用于提供附加信息的环境。它包含了领域（realm）和模块映射（module map），以及 HTML 的特定信息，如来源（origin）等。对现有设置对象的追踪保证了浏览器知道用户给定的哪些代码片段需要使用。

为了更好地说明这一点，我们在这里进一步探讨领域是如何引发问题的。我们可以粗略地认为**领域**是一个全局对象。其独特之处在于，它拥有运行 JavaScript 代码所需的所有信息。这包括像 `Array` 和 `Error` 这样的对象。每一个设置对象都有自己的“副本”，而且它们与副本之间是不共享的。这可能会导致一些与 promise 相关的意外行为。为了解决这个问题，我们需要追踪**现有设置对象**（incumbent settings object）。它表示负责用户某个函数调用工作的特定信息。

我们可以尝试在文档中嵌入 `<iframe>`，并让其与父级上下文通信。由于所有的 web API 都有现有设置对象，下面的代码能够在所有的浏览器中运行：

```html
<!doctype html> <iframe></iframe>
<!-- 在这里有一个 realm -->
<script>
 // 这里也有一个 realm
 const bound = frames[0].postMessage.bind(frames[0], "一些数据", "\*");
 // bound 是一个内置函数，栈中没有用户代码，因此我们应该使用哪个 realm 呢？
 setTimeout(bound);
 // 这仍然可以工作，因为我们在栈上使用最新的 realm（即现有的 realm）
</script>
```

同样的概念也适用于 promise。如果我们稍加修改上面的示例，我们就能得到这个：

```html
<!doctype html> <iframe></iframe>
<!-- 在这里有一个领域 -->
<script>
 // 这里也有一个领域
 const bound = frames[0].postMessage.bind(frames[0], "一些数据", "\*");
 // bound 是一个内置函数，栈中没有用户代码，所以我们应该使用哪个领域？
 Promise.resolve(undefined).then(bound);
 // 这仍然可以工作，因为我们在栈上使用最新的领域（即现有领域）
</script>
```

如果我们修改代码，使用文档中的 `<iframe>` 来监听发送的消息，我们可以观察到现有设置对象的影响：

```html
<!-- y.html -->
<!doctype html>
<iframe src="x.html"></iframe>
<script>
 const bound = frames[0].postMessage.bind(frames[0], "一些数据", "\*");
 Promise.resolve(undefined).then(bound);
</script>
```

```html
<!-- x.html -->
<!doctype html>
<script>
 window.addEventListener(
 "message",
 (event) => {
 document.querySelector("#text").textContent = "hello";
 // 此代码将仅在跟踪现有设置对象的浏览器中运行
 console.log(event);
 },
 false,
 );
</script>
```

在上面的示例中，`<iframe>` 仅在现有设置对象被追踪时才会被更新。这是因为在不追踪的情况下，我们可能会使用错误的环境发送消息。

**备注：** 目前，Firefox 完全实现了现有领域追踪，Chrome 和 Safari 仅部分实现。

规范
--

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

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

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

参见
--

* `core-js` 中 `Promise` 的 Polyfill
* 使用 promise
* Promises/A+ 规范
* JavaScript Promise：简介
* Domenic Denicola：回调、Promise 和协程——JavaScript 中的异步编程模式


Uint16Array - JavaScript

Uint16Array
===========

`该 Uint16Array` 类型数组表示 16 位无符号整数，按平台字节顺序排列。如果需要控制字节顺序，请使用 DataView 代替。内容被初始化为 0。建立后，就可以使用对象的方法或使用标准数组索引语法（即使用括号表示法）引用数组中的元素。

构造函数
----

`Uint16Array()` (en-US)

创建一个新的 `Uint16Array` 对象。

静态属性
----

`Uint16Array.BYTES_PER_ELEMENT`

返回元素大小的数值。在 `Uint16Array` 情况下为 `2`。

静态方法
----

`Uint16Array.from()`

从类数组或可迭代对象创建一个新的 `Uint16Array` 。可参阅 `Array.from()`。

`Uint16Array.of()`

创建一个新的具有可变参数数目的 `Uint16Array` 。可参阅 `Array.of()`。

实例属性
----

`Uint16Array.prototype.buffer`

返回 `Uint16Array` 在构造时固定引用的 `ArrayBuffer` 。因此是 **只读的**。

`Uint16Array.prototype.byteLength`

返回 `Uint16Array` 从 `ArrayBuffer`开始的长度（以字节为单位）。在构建时固定，因此是 **只读的。**

`Uint16Array.prototype.byteOffset`

返回 `Uint16Array` 从 `ArrayBuffer`开始的偏移量（以字节为单位）。在构建时固定，因此是 **只读的。**

`Uint16Array.prototype.length`

返回 `Uint16Array` 中包含的元素数量。在构建时固定，因此是 **只读的。**

实例方法
----

`Uint16Array.prototype.copyWithin()`

复制数组中的一序列数组元素。可参阅 `Array.prototype.copyWithin()`。

`Uint16Array.prototype.entries()`

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

`Uint16Array.prototype.every()`

测试数组中的所有元素是否通过函数提供的测试。可参阅 `Array.prototype.every()`。

`Uint16Array.prototype.fill()`

使用静态值填充从开始索引到结束索引的数组的所有元素。可参阅 `Array.prototype.fill()`。

`Uint16Array.prototype.filter()`

根据所提供的过滤函数，将所有返回 true 的所有元素创建一个新的数组。可参阅 `Array.prototype.filter()`。

`Uint16Array.prototype.find()`

返回数组中满足所提供的测试函数的值，没有则返回 `undefined` 。可参阅 `Array.prototype.find()`。

`Uint16Array.prototype.findIndex()`

返回数组中满足所提供的测试函数的索引，没有则返回 -1。可参阅 `Array.prototype.findIndex()`。

`Uint16Array.prototype.forEach()`

为数组中的每个元素调用一个函数。可参阅 `Array.prototype.forEach()`。

`Uint16Array.prototype.includes()`

判断数组中是否包含某个元素，有则返回 `true` ，无则返回 `false` 。可参阅 `Array.prototype.includes()`。

`Uint16Array.prototype.indexOf()`

返回数组中等于指定的值的元素第一个（最小）索引，没有则返回 -1。可参阅 `Array.prototype.indexOf()`。

`Uint16Array.prototype.join()`

将数组中的所有元素连接成字符串。可参阅 `Array.prototype.join()`。

`Uint16Array.prototype.keys()`

f 返回一个新的 `Array Iterator` ，其包含数组中每个索引的键。可参阅 `Array.prototype.keys()`。

`Uint16Array.prototype.lastIndexOf()`

返回数组中最后一个（最大的）与指定值相等的元素的索引，没有则返回 -1。可参阅 `Array.prototype.lastIndexOf()`。

`Uint16Array.prototype.map()`

返回一个根据所提供的函数对每个元素处理后的新数组。可参阅 `Array.prototype.map()`。

`Uint16Array.prototype.reduce()`

根据所提供的函数对累加器和数组的每一个值（从左到右）进行处理，并返回最后调用函数的值。可参阅 `Array.prototype.reduce()`。

`Uint16Array.prototype.reduceRight()`

根据所提供的函数对累加器和数组的每一个值（从右到左）进行处理，并返回最后调用函数的值。可参阅 `Array.prototype.reduceRight()`。

`Uint16Array.prototype.reverse()`

颠倒数组的顺序，使第一个元素成为最后一个，最后一个元素成为第一个。可参阅 `Array.prototype.reverse()`。

`Uint16Array.prototype.set()`

在类型化数组中存储多个值，从指定数组中读取输入值。

`Uint16Array.prototype.slice()`

截取数组中的一部分并作为新数组返回。可参阅 `Array.prototype.slice()`。

`Uint16Array.prototype.some()`

判读数组中是否至少有一个元素满足所提供的测试函数，有则返回 true。可参阅 `Array.prototype.some()`。

`Uint16Array.prototype.sort()`

对数组中的元素进行排序并返回排序后的数组。可参阅 `Array.prototype.sort()`。

`Uint16Array.prototype.subarray()`

从给定的开始与结束元素索引中返回一个新的 `Uint16Array` 。

`Uint16Array.prototype.values()`

返回一个新的 `Array Iterator` 对象，该对象包含数组中的每个索引的值。可参阅 `Array.prototype.values()`.

`Uint16Array.prototype.toLocaleString()`

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

`Uint16Array.prototype.toString()`

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

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

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

示例
--

### Different ways to create a Uint16Array

```js
// 长度
var uint16 = new Uint16Array(2);
uint16[0] = 42;
console.log(uint16[0]); // 42
console.log(uint16.length); // 2
console.log(uint16.BYTES\_PER\_ELEMENT); // 2

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

// 另一个类型数组
var x = new Uint16Array([21, 31]);
var y = new Uint16Array(x);
console.log(y[0]); // 21

// 一个 ArrayBuffer
var buffer = new ArrayBuffer(8);
var z = new Uint16Array(buffer, 0, 4);

// 可迭代
var iterable = (function\* () {
  yield\* [1, 2, 3];
})();
var uint16 = new Uint16Array(iterable);
// Uint16Array[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 typed arrays
* `ArrayBuffer`
* `DataView`


Number - JavaScript

Number
======

**`Number`** 值表示像 `37` 或 `-9.25` 这样的浮点数。

`Number` 构造函数包含常量和处理数值的方法。其他类型的值可以使用 `Number()` 函数转换为数字。

描述
--

数值通常以字面量形式表示。如 `255` 或 `3.14159`。词法文法包含更详细的参考。

```js
255; // 二百五十五
255.0; // 相同的数字
255 === 255.0; // true
255 === 0xff; // true（十六进制表示）
255 === 0b11111111; // true（二进制表示）
255 === 0.255e3; // true（十进制指数记数法）
```

在 JavaScript 代码中，像 `37` 这样的数字字面量是浮点数值，而不是整数。在常见的日常使用中，JavaScript 没有单独的整数类型。（JavaScript 还有一个 `BigInt` 类型，但它并不是为了取代 Number 而设计的，`37` 仍然是一个数字，而不是一个 BigInt。）

当作为一个函数使用时，`Number(value)` 将字符串或者其他值转换到 Number 类型。如果该值不能被转换，它会返回 `NaN`。

```js
Number("123"); // 返回数字 123
Number("123") === 123; // true

Number("unicorn"); // NaN
Number(undefined); // NaN
```

### Number 编码

JavaScript 的 `Number` 类型是一个双精度 64 位二进制格式 IEEE 754 值，类似于 Java 或者 C# 中的 `double`。这意味着它可以表示小数值，但是存储的数字的大小和精度有一些限制。简而言之，IEEE 754 双精度浮点数使用 64 位来表示 3 个部分：

* 1 位用于表示*符号（sign）*（正数或者负数）
* 11 位用于表示*指数（exponent）*（-1022 到 1023）
* 52 位用于表示*尾数（mantissa）*（表示 0 和 1 之间的数值）

尾数（也称为*有效数*）是表示实际值（有效数字）的数值部分。指数是尾数应乘以的 2 的幂次。将其视为科学计数法：

Number
=
(

−
1

)
sign

⋅
(
1
+
mantissa
)
⋅

2
exponent

\text{Number} = ({-1})^{\text{sign}} \cdot (1 + \text{mantissa}) \cdot 2^{\text{exponent}}

尾数使用 52 比特存储，在二进制小数中解释为 `1.…` 之后的数字。因此，尾数的精度是 2-52（可以通过 `Number.EPSILON` 获得），或者十进制数小数点后大约 15 到 17 位；超过这个精度的算术会受到舍入的影响。

一个数值可以容纳的最大值是 21024 - 1（指数为 1023，尾数为基于二进制的 0.1111…），可以通过 `Number.MAX_VALUE` 获得。超过这个值的数会被替换为特殊的数值常量 `Infinity`。

只有在 -253 + 1 到 253 - 1 范围内（闭区间）的整数才能在不丢失精度的情况下被表示（可通过 `Number.MIN_SAFE_INTEGER` 和 `Number.MAX_SAFE_INTEGER` 获得），因为尾数只能容纳 53 位（包括前导 1）。

有关这部份的更多详细信息，请参阅 ECMAScript 标准。

### Number 强制转换

许多期望数值的内置操作首先将其参数转换为数字（这在很大程度上解释了为什么 `Number` 对象的行为类似于数字原始值）。该操作可以总结如下：

* Number 将按原样返回
* `undefined` 转换为 `NaN`。
* `null` 转换为 `0`。
* `true` 转换为 `1`；`false` 转换为 `0`。
* 字符串将被假定为包含数字字面量，并通过解析它们来转换。解析失败会得到 `NaN`。与实际数字字面量相比，它们有一些细微的差别：
    + 前导和尾随的空格/换行符会被忽略。
    + 前导的数字 `0` 不会导致该数值成为八进制字面量（或在严格模式下被拒绝）。
    + `+` 和 `-` 允许出现在字符串的开头以指示其符号。（在实际代码中，它们“看起来像”文字的一部分，但实际上是独立的一元运算符。）然而，该标志只能出现一次，并且后面不能跟空格。
    + `Infinity` 和 `-Infinity` 被当作是字面量。在实际代码中，它们是全局变量。
    + 空字符串或仅包含空格的字符串转换为 `0`。
    + 不允许使用数字分隔符。
* BigInt 抛出 `TypeError`，以防止意外的强制隐式转换导致精度损失。
* Symbol 抛出 `TypeError`。
* 对象首先通过按顺序调用它们的 `[@@toPrimitive]()`（使用 `"number"` 提示）、`valueOf()` 和 `toString()` 方法将其转换为原始值。然后将得到的原始值转换为数字。

有两种方法可以在 JavaScript 中实现几乎相同的效果。

* 一元加：`+x` 完全按照上面的数值强制转换步骤来转换 `x`。
* `Number()` 函数：`Number(x)` 使用相同的算法转换 `x`，除了 BigInt 不会抛出 `TypeError`，而是返回其数字值，并且可能导致精度损失。

`Number.parseFloat()` 和 `Number.parseInt()` 与 `Number()` 相似，但只能转换字符串，并且解析规则略有不同。例如，`parseInt()` 无法识别小数点，`parseFloat()` 无法识别 `0x` 前缀。

#### 整数转换

某些操作需要整数作为参数，尤其是那些与数组/字符串索引、日期/时间组件和数字进制相关的操作。在执行上述数值强制转换步骤后，结果将被截断为整数（即舍弃小数部份）。如果数值为 ±Infinity，则按原样返回。如果数值是 `NaN` 或 `-0`，则返回为 `0`。因此，结果总是整数（不包括 `-0`）或 ±Infinity。

值得注意的是，当转换到整数时，`undefined` 和 `null` 都会变成 `0`，因为 `undefined` 被转换为 `NaN`，`NaN` 也变成了 `0`。

#### 固定宽度数值转换

JavaScript 有一些较低级别的函数，用于处理整数的二进制编码，最值得注意的是按位运算和 `TypedArray` 对象。按位运算总是将操作数转换为 32 位整数。在这些情况下，将值转换为数字后，数字将首先截断小数部分，然后再取整数的二进制的补码编码中的最低几位来将数值标准化为给定的宽度。

```js
new Int32Array([1.1, 1.9, -1.1, -1.9]); // Int32Array(4) [ 1, 1, -1, -1 ]

new Int8Array([257, -257]); // Int8Array(2) [ 1, -1 ]
// 257 = 0001 0000 0001
// = 0000 0001（模 2^8）
// = 1
// -257 = 1110 1111 1111
// = 1111 1111（模 2^8）
// = -1（带符号整数）

new Uint8Array([257, -257]); // Uint8Array(2) [ 1, 255 ]
// -257 = 1110 1111 1111
// = 1111 1111（模 2^8）
// = 255（无符号整数）
```

构造函数
----

`Number()`

创建一个新的 `Number` 值。

当 `Number` 作为一个构造函数（用 `new`）被调用时，它会创建一个 `Number` 对象，该对象**不是**一个原始值。例如，`typeof new Number(42) === "object"`，并且 `new Number(42) !== 42`（尽管 `new Number(42) == 42`）。

**警告：** 你应该很少发现自己将 `Number` 作为构造函数使用。

静态属性
----

`Number.EPSILON`

两个可表示数之间的最小间隔。

`Number.MAX_SAFE_INTEGER`

JavaScript 中最大的安全整数（253 - 1）。

`Number.MAX_VALUE`

能表示的最大正数。

`Number.MIN_SAFE_INTEGER`

JavaScript 中最小的安全整数（-(253 - 1)）。

`Number.MIN_VALUE`

能表示的最小正数即最接近 0 的正数（实际上不会变成 0）。

`Number.NaN`

特殊的“**N**ot **a** **N**umber”（非数字）值。

`Number.NEGATIVE_INFINITY`

特殊的负无穷大值，在溢出时返回该值。

`Number.POSITIVE_INFINITY`

特殊的正无穷大值，在溢出时返回该值。

静态方法
----

`Number.isFinite()`

判断传入的值是否是有限数。

`Number.isInteger()`

判断传入的值是否为整数

`Number.isNaN()`

判断传入的值是否为 `NaN`。

`Number.isSafeInteger()`

判断传入的值是否为安全整数（数值在 -(253 - 1) 到 253 - 1 之间）。

`Number.parseFloat()`

和全局函数 `parseFloat()` 相同。

`Number.parseInt()`

和全局函数 `parseInt()` 相同。

实例属性
----

这些属性是在 `Number.prototype` 上定义的，并由所有 `Number` 实例共享。

`Number.prototype.constructor`

创建实例对象的构造函数。对于 `Number` 实例，初始值为 `Number` 构造函数。

实例方法
----

`Number.prototype.toExponential()`

返回使用指数表示法表示数值的字符串。

`Number.prototype.toFixed()`

返回使用定点表示法表示数值的字符串。

`Number.prototype.toLocaleString()`

返回数值在特定语言环境下表示的字符串。重写了 `Object.prototype.toLocaleString()` 方法。

`Number.prototype.toPrecision()`

返回数值使用定点表示法或指数表示法至指定精度的字符串。

`Number.prototype.toString()`

返回一个代表给定对象的字符串，基于指定的*基数*（radix）。重写了 `Object.prototype.toString()` 方法。

`Number.prototype.valueOf()`

返回指定对象的原始值。重写了 `Object.prototype.valueOf()` 方法。

示例
--

### 使用 Number 对象给数字变量赋值

下例使用 `Number` 对象的属性给几个数字变量赋值：

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

### 整数类型的范围

以下示例显示了可以表示为 `Number` 对象的最小和最大整数值。

```js
const biggestInt = Number.MAX\_SAFE\_INTEGER; // (2\*\*53 - 1) => 9007199254740991
const smallestInt = Number.MIN\_SAFE\_INTEGER; // -(2\*\*53 - 1) => -9007199254740991
```

当解析已序列化为 JSON 的数据时，如果 JSON 解析器将它们强制为 `Number` 类型，超出此范围的整数值可能会被损坏。

一个可能的变通办法是使用 `String`。

较大的数值可以使用 `BigInt` 类型表示。

### 使用 Number() 转换 Date 对象

以下示例使用 `Number` 作为函数将 `Date` 对象转换为数值：

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

这将输出 `819199440000`。

### 将数字字符串和 null 转换为数值

```js
Number("123"); // 123
Number("123") === 123; // true
Number("12.3"); // 12.3
Number("12.00"); // 12
Number("123e-1"); // 12.3
Number(""); // 0
Number(null); // 0
Number("0x11"); // 17
Number("0b11"); // 3
Number("0o11"); // 9
Number("foo"); // NaN
Number("100a"); // NaN
Number("-Infinity"); // -Infinity
```

规范
--

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

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

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

参见
--

* 在 `core-js` 中现代 `Number` 行为的 Polyfill（支持二进制和八进制字面量）
* `NaN`
* 算术运算符
* `Math`
* `BigInt`


Map - JavaScript

Map
===

**`Map`** 对象保存键值对，并且能够记住键的原始插入顺序。任何值（对象或者原始值）都可以作为键或值。

尝试一下
----

描述
--

`Map` 对象是键值对的集合。`Map` 中的一个键**只能出现一次**；它在 `Map` 的集合中是独一无二的。`Map` 对象按键值对迭代——一个 `for...of` 循环在每次迭代后会返回一个形式为 `[key, value]` 的数组。迭代按*插入顺序*进行，即键值对按 `set()` 方法首次插入到集合中的顺序（也就是说，当调用 `set()` 时，map 中没有具有相同值的键）进行迭代。

规范要求 map 实现“平均访问时间与集合中的元素数量呈次线性关系”。因此，它可以在内部表示为哈希表（使用 O(1) 查找）、搜索树（使用 O(log(N)) 查找）或任何其他数据结构，只要复杂度小于 O(N)。

### 键的相等

键的比较基于零值相等算法。（它曾经使用同值相等，将 `0` 和 `-0` 视为不同。检查浏览器兼容性。）这意味着 `NaN` 是与 `NaN` 相等的（虽然 `NaN !== NaN`），剩下所有其他的值是根据 `===` 运算符的结果判断是否相等。

### `Object` 和 `Map` 的比较

`Object` 和 `Map` 类似的是，它们都允许你按键存取一个值、删除键、检测一个键是否绑定了值。因此（并且也没有其他内建的替代方式了）过去我们一直都把对象当成 `Map` 使用。

不过 `Map` 和 `Object` 有一些重要的区别，在下列情况中使用 `Map` 会是更好的选择：

|  | Map | Object |
| --- | --- | --- |
| 意外的键 | `Map` 默认不包含任何键。它只包含显式存入的键值对。 | `Object` 有原型，因此它包含默认的键，如果不小心的话，它们可能会与你自己的键相冲突。

**备注：**这可以通过使用 `Object.create(null)` 来绕过，但很少这样做。
 |
| 安全性 | `Map` 可以安全地与用户提供的键值一起使用。 | 在 `Object` 上设置用户提供的键值对可能会允许攻击者覆盖对象的原型，这可能会导致对象注入攻击。就像意外的键问题一样，这也可以通过使用 `null` 原型对象来缓解。 |
| 键的类型 | `Map` 的键可以为任何值（包括函数、对象或任何原始值）。 | `Object` 的键必须为 `String` 或 `Symbol`。 |
| 键的顺序 | `Map` 中的键以简单、直接的方式排序：`Map` 对象按照插入的顺序迭代条目、键和值。 | 尽管现在普通的 `Object` 的键是有序的，但情况并非总是如此，并且其排序比较复杂的。因此，最好不要依赖属性的顺序。
该顺序最初仅在 ECMAScript 2015 中为自有属性定义；ECMAScript 2020 还定义了继承属性的顺序。但请注意，没有单一机制可以迭代对象的**所有**属性；各种机制各自包含不同的属性子集。（`for-in` 仅包含可枚举的字符串键属性；`Object.keys` 仅包含可枚举的自有字符串键属性；`Object.getOwnPropertyNames` 包括自有的字符串键属性，即使是不可枚举的；`Object.getOwnPropertySymbols` 仅对 `Symbol` 键属性执行相同的操作，等等。） |
| 大小 | `Map` 中的项目数量很容易从其 `size` 属性中获得。 | 确定 `Object` 中的项目数量通常更麻烦，效率也较低。一种常见的方法是通过获取 `Object.keys()` 返回的数组的长度。 |
| 迭代 | `Map` 是可迭代对象，所以它可以直接迭代。 | `Object` 没有实现迭代协议，因此对象默认情况下不能直接通过 JavaScript 的 for...of 语句进行迭代。

**备注：**
* 一个对象可以实现迭代协议，或者你可以使用 `Object.keys` 或 `Object.entries` 来获取一个对象的可迭代对象。
* for...in 语句允许你迭代对象的*可枚举*属性。

 |
| 性能 | 在涉及频繁添加和删除键值对的场景中表现更好。 | 未针对频繁添加和删除键值对进行优化。 |
| 序列化和解析 | 没有对序列化或解析的原生支持。
（但你可以通过使用 `JSON.stringify()` 及其 *replacer* 参数和 `JSON.parse()` 及其 *reviver* 参数来为 `Map` 构建自己的序列化和解析支持。参见 Stack Overflow 问题 How do you JSON.stringify an ES6 Map?）。 | 原生支持使用 `JSON.stringify()` 序列化 `Object` 到 JSON。
原生支持使用 `JSON.parse()` 解析 JSON 为 `Object`。 |

### 设置对象属性

设置对象属性同样适用于 Map 对象，但容易造成困扰。

即，以下的代码能够正常运行（但不推荐）：

```js
const wrongMap = new Map();
wrongMap["bla"] = "blaa";
wrongMap["bla2"] = "blaaa2";

console.log(wrongMap); // Map { bla: 'blaa', bla2: 'blaaa2' }
```

但这种设置属性的方式不会改变 Map 的数据结构。它使用的是通用对象的特性。`'bla'` 的值未被存储在 Map 中，无法被查询到。其他的对这一数据的操作也会失败：

```js
wrongMap.has("bla"); // false
wrongMap.delete("bla"); // false
console.log(wrongMap); // Map { bla: 'blaa', bla2: 'blaaa2' }
```

正确的存储数据到 Map 中的方式是使用 `set(key, value)` 方法。

```js
const contacts = new Map();
contacts.set("Jessie", { phone: "213-555-1234", address: "123 N 1st Ave" });
contacts.has("Jessie"); // true
contacts.get("Hilary"); // undefined
contacts.set("Hilary", { phone: "617-555-4321", address: "321 S 2nd St" });
contacts.get("Jessie"); // {phone: "213-555-1234", address: "123 N 1st Ave"}
contacts.delete("Raymond"); // false
contacts.delete("Jessie"); // true
console.log(contacts.size); // 1
```

### 类 Map 浏览器 API

**浏览器类 Map 对象**（或称为“maplike 对象”）是其行为在很多方面都类似于 `Map` 的 Web API 接口。

就像 `Map` 一样，对象中的条目可以以添加的顺序迭代。类似 `Map` 的对象和 `Map` 具有相同的属性和方法。但是，与 `Map` 不同的是，它们仅允许每个条目中的键和值具有特定预定义的类型。

允许的类型规范的 IDL 定义给出。例如，`RTCStatsReport` 是一个类似 `Map` 的对象，必须使用字符串作为键，对象作为值。这是在规范 IDL 中定义的：

```webidl
interface RTCStatsReport {
  readonly maplike<DOMString, object>;
};
```

类 `Map` 对象可以是只读的，也可以是可写的（参见上面 IDL 中的 `readonly` 关键字）。

* 只读的类 `Map` 对象具有 `size` 属性，以及这些方法：`entries()`、`forEach()`、`keys()`、`values()` 和 `@@iterator` 。
* 可写的类 `Map` 对象还额外具有这些方法：`clear()`、`delete()` 和 `set()`。

除了对键和值类型的限制外，其方法和属性的行为与 `Map` 中的对应实体相同。

以下是浏览器中只读的类 `Map` 对象的示例：

* `AudioParamMap` (en-US)
* `RTCStatsReport`
* `EventCounts` (en-US)
* `KeyboardLayoutMap` (en-US)
* `MIDIInputMap` (en-US)
* `MIDIOutputMap` (en-US)
构造函数
----

`Map()`

创建 `Map` 对象。

静态属性
----

`Map[@@species]`

用于创建派生对象的构造函数。

静态方法
----

`Map.groupBy()`

根据提供的回调函数返回的值将给定的可迭代对象分组。最终返回的 `Map` 对象使用测试函数返回的唯一值作为键，可用于获取每个组的元素数组。

实例属性
----

这些属性在 `Map.prototype` 上定义，并由所有 `Map` 实例共享。

`Map.prototype.constructor`

创建实例对象的构造函数。对于 `Map` 实例，初始值为 `Map` 构造函数。

`Map.prototype.size`

返回 `Map` 对象中的键值对数量。

`Map.prototype[@@toStringTag]`

`@@toStringTag` 属性的初始值是字符串 `"Map"`。该属性在 `Object.prototype.toString()` 中使用。

实例方法
----

`Map.prototype.clear()`

移除 `Map` 对象中所有的键值对。

`Map.prototype.delete()`

移除 `Map` 对象中指定的键值对，如果键值对存在并成功被移除，返回 `true`，否则返回 `false`。调用 `delete` 后再调用 `map.has(key)` 将返回 `false`。

`Map.prototype.entries()`

返回一个新的迭代器对象，其包含 `Map` 对象中所有键值对 `[key, value]` 二元数组，以插入顺序排列。

`Map.prototype.forEach()`

以插入顺序为 `Map` 对象中的每个键值对调用一次 `callbackFn`。如果为 `forEach` 提供了 `thisArg` 参数，则它将作为每一次 callback 的 `this` 值。

`Map.prototype.get()`

返回与指定的键 `key` 关联的值，若不存在关联的值，则返回 `undefined`。

`Map.prototype.has()`

返回一个布尔值，用来表明 `Map` 对象中是否存在与指定的键 `key` 关联的值。

`Map.prototype.keys()`

返回一个新的迭代器对象，其包含 `Map` 对象中所有元素的键，以插入顺序排列。

`Map.prototype.set()`

在 `Map` 对象中设置与指定的键 `key` 关联的值，并返回 `Map` 对象。

`Map.prototype.values()`

返回一个新的迭代对象，其中包含 `Map` 对象中所有的值，并以插入 `Map` 对象的顺序排列。

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

返回一个新的迭代器对象，其包含 `Map` 对象中所有元素 `[key, value]` 二元数组，以插入顺序排列。

示例
--

### 使用 Map 对象

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

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

// 添加键
myMap.set(keyString, "和键'a string'关联的值");
myMap.set(keyObj, "和键 keyObj 关联的值");
myMap.set(keyFunc, "和键 keyFunc 关联的值");

console.log(myMap.size); // 3

// 读取值
console.log(myMap.get(keyString)); // "和键'a string'关联的值"
console.log(myMap.get(keyObj)); // "和键 keyObj 关联的值"
console.log(myMap.get(keyFunc)); // "和键 keyFunc 关联的值"

console.log(myMap.get("a string")); // "和键'a string'关联的值"，因为 keyString === 'a string'
console.log(myMap.get({})); // undefined，因为 keyObj !== {}
console.log(myMap.get(function () {})); // undefined，因为 keyFunc !== function () {}
```

### 将 NaN 作为 Map 的键

`NaN` 也可以作为键。虽然 `NaN` 与任何值甚至与自己都不相等（`NaN !== NaN` 返回 true），但是因为所有的 `NaN` 的值都是无法区分的，所以下面的例子成立：

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

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

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

### 使用 for...of 迭代 Map

`Map` 可以使用 `for...of` 循环来实现迭代：

```js
const myMap = new Map();
myMap.set(0, "zero");
myMap.set(1, "one");

for (const [key, value] of myMap) {
  console.log(`${key} = ${value}`);
}
// 0 = zero
// 1 = one

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

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

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

### 使用 forEach() 迭代 Map

`Map` 也可以通过 `forEach()` 方法迭代：

```js
myMap.forEach((value, key) => {
  console.log(`${key} = ${value}`);
});
// 0 = zero
// 1 = one
```

### Map 与数组对象的关系

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

// 使用常规的 Map 构造函数可以将一个二维的键值对数组转换成一个 Map 对象
const myMap = new Map(kvArray);

console.log(myMap.get("key1")); // "value1"

// 使用 Array.from 函数可以将一个 Map 对象转换成一个二维的键值对数组
console.log(Array.from(myMap)); // 输出和 kvArray 相同的数组

// 更简洁的方法来做如上同样的事情，使用展开运算符
console.log([...myMap]);

// 或者在键或者值的迭代器上使用 Array.from，进而得到只含有键或者值的数组
console.log(Array.from(myMap.keys())); // 输出 ["key1", "key2"]
```

### 复制或合并 Maps

`Map` 能像数组一样被复制：

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

const clone = new Map(original);

console.log(clone.get(1)); // one
console.log(original === clone); // false. 浅比较 不为同一个对象的引用
```

**备注：** 请记住，*数据本身*未被克隆。

`Map` 对象间可以进行合并，但是会保持键的唯一性。

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

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

// 合并两个 Map 对象时，如果有重复的键值，则后面的会覆盖前面的。
// 展开语法本质上是将 Map 对象转换成数组。
const merged = new Map([...first, ...second]);

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

`Map` 对象也能与数组合并：

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

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

// Map 对象同数组进行合并时，如果有重复的键值，则后面的会覆盖前面的。
const merged = new Map([...first, ...second, [1, "eins"]]);

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

规范
--

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

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

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

参见
--

* `core-js` 中 `Map` 的 polyfill
* `Set`
* `WeakMap`
* `WeakSet`


globalThis - JavaScript

globalThis
==========

全局属性 `globalThis` 包含全局的 `this` 值，类似于全局对象（global object）。

尝试一下
----

| `globalThis` 的属性特性 |
| --- |
| 可写 | 是 |
| 可枚举 | 否 |
| 可配置 | 是 |

语法
--
```

globalThis
```

描述
--

在以前，从不同的 JavaScript 环境中获取全局对象需要不同的语句。在 Web 中，可以通过 `window`、`self` 或者 `frames` 取到全局对象，但是在 Web Workers 中，只有 `self` 可以。在 Node.js 中，它们都无法获取，必须使用 `global`。

在松散模式下，可以在函数中返回 `this` 来获取全局对象，但是在严格模式和模块环境下，`this` 会返回 `undefined`。你也可以使用 `Function('return this')()`，但那些禁用`eval()`的环境，如在浏览器中的CSP，不允许这样使用`Function`。

`globalThis` 提供了一个标准的方式来获取不同环境下的全局 `this` 对象（也就是全局对象自身）。不像 `window` 或者 `self` 这些属性，它确保可以在有无窗口的各种环境下正常工作。所以，你可以安心的使用 `globalThis`，不必担心它的运行环境。为便于记忆，你只需要记住，全局作用域中的 `this` 就是 `globalThis`。

### HTML 与 WindowProxy

在很多引擎中， `globalThis` 被认为是真实的全局对象的引用，但是在浏览器中，由于 iframe 以及跨窗口安全性的考虑，它实际引用的是真实全局对象（不可以被直接访问）的 `Proxy` 代理。在通常的应用中，很少会涉及到代理与对象本身的区别，但是也需要加以注意。

### 命名

并没有采用一些更常见的命名方式，如 `self` 和 `global`，这是为了避免影响现存代码的兼容性。更多相关信息可以查看 language proposal's "naming" document 。

示例
--

在 `globalThis` 之前，获取某个全局对象的唯一方式就是 `Function('return this')()`，但是这在某些情况下会违反 CSP 规则，所以，es6-shim 使用了类似如下的方式：

```js
var getGlobal = function () {
  if (typeof self !== "undefined") {
    return self;
  }
  if (typeof window !== "undefined") {
    return window;
  }
  if (typeof global !== "undefined") {
    return global;
  }
  throw new Error("unable to locate global object");
};

var globals = getGlobal();

if (typeof globals.setTimeout !== "function") {
  // 此环境中没有 setTimeout 方法！
}
```

但是有了 `globalThis` 之后，只需要：

```js
if (typeof globalThis.setTimeout !== "function") {
  // 此环境中没有 setTimeout 方法！
}
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-globalthis |

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

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


NaN - JavaScript

NaN
===

全局属性 **`NaN`** 是一个表示非数字的值。

| `NaN` 的属性特性 |
| --- |
| 可写 | 否 |
| 可枚举 | 否 |
| 可配置 | 否 |

尝试一下
----

描述
--

`NaN`是*全局对象*的一个属性。换句话说，它是全局作用域中的一个变量。

`NaN` 的初始值不是数字——与 `Number.NaN` 的值相同。在现代浏览器中，`NaN` 是一个不可配置、不可写的属性。即使不是这样，也要避免重写它。在程序中很少使用 `NaN`。

有五种不同类型的操作返回 `NaN`：

* 失败的数字转换（例如，显式转换，如 `parseInt("blabla")`、`Number(undefined)`，或隐式转换，如 `Math.abs(undefined)`）
* 计算结果不是实数的数学运算（例如，`Math.sqrt(-1)`）
* 不定式（例如，`0 * Infinity`、`1 ** Infinity`、`Infinity / Infinity`、`Infinity - Infinity`）
* 一个操作数被强制转换为 `NaN` 的方法或表达式（例如，`7 ** NaN`、`7 * "blabla"`）——这意味着 `NaN` 具有传染性
* 将无效值表示为数字的其他情况（例如，无效的 Date `new Date("blabla").getTime()`、`"".charCodeAt(1)`）

`NaN` 及其行为不是 JavaScript 发明的。它在浮点运算中的语义（包括 `NaN !== NaN`）是由 IEEE 754 指定的。`NaN` 的行为包括：

* 如果 `NaN` 涉及数学运算（但不涉及位运算），结果通常也是 `NaN`。（参见下面的反例。）
* 当 `NaN` 是任何关系比较（`>`, `<`, `>=`, `<=`）的操作数之一时，结果总是 `false`。
* `NaN` 不等于（通过 `==`、`!=`、`===` 和 `!==`）任何其他值——包括与另一个 `NaN` 值。

`NaN` 也是 JavaScript 中的假值之一。

示例
--

### 针对 NaN 的测试

要判断一个值是否为 `NaN`，可以使用 `Number.isNaN()` 或 `isNaN()` 来最清楚地确定一个值是否为 `NaN`——或者，因为 `NaN` 是唯一与自身不相等的值，所以你可以执行类似 `x !== x` 这样的自我比较。

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

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

但是，请注意 `isNaN()` 和 `Number.isNaN()` 之间的区别：如果当前值是 `NaN`，或者将其强制转换为数字后将是 `NaN`，则前者将返回 `true`。而后者仅当值当前为 `NaN` 时才为 `true`：

```js
isNaN("hello world"); // true
Number.isNaN("hello world"); // false
```

出于同样的原因，使用 BigInt 值时 `isNaN()` 会抛出错误，而 `Number.isNaN()` 不会：

```js
isNaN(1n); // TypeError: Conversion from 'BigInt' to 'number' is not allowed.
Number.isNaN(1n); // false
```

此外，一些数组方法不能找到 `NaN`，而另一些可以。也就是说，查找索引的（`indexOf()`、`lastIndexOf()`）不能找到 `NaN`，而查找值的（`includes()`）可以：

```js
const arr = [2, 4, NaN, 12];
arr.indexOf(NaN); // -1
arr.includes(NaN); // true

// Methods accepting a properly defined predicate can always find NaN
arr.findIndex((n) => Number.isNaN(n)); // 2
```

有关 `NaN` 及其比较的更多信息，请参阅相等性判断。

### 明显不同的 NaN 值

这是 `NaN` 与自身不平等的动机。可以用不同的二进制表示生成两个都是 `NaN` 的浮点数，因为在 IEEE 754 编码中，任何指数为 `0x7ff` 且尾数非零的浮点数都是 `NaN`。在 JavaScript 中，你可以使用类型化数组来进行位操作。

```js
const f2b = (x) => new Uint8Array(new Float64Array([x]).buffer);
const b2f = (x) => new Float64Array(x.buffer)[0];
// Get a byte representation of NaN
const n = f2b(NaN);
// Change the first bit, which is the sign bit and doesn't matter for NaN
n[0] = 1;
const nan2 = b2f(n);
console.log(nan2); // NaN
console.log(Object.is(nan2, NaN)); // true
console.log(f2b(NaN)); // Uint8Array(8) [0, 0, 0, 0, 0, 0, 248, 127]
console.log(f2b(nan2)); // Uint8Array(8) [1, 0, 0, 0, 0, 0, 248, 127]
```

### 默默逃离 NaN

`NaN` 通过数学运算进行传播，因此通常在计算结束时测试 `NaN` 一次就足以检测错误条件。`NaN` 被静默转义的唯一情况是使用指数为 `0` 求幂时，它立即返回 `1` 而不测试基数的值。

```js
NaN \*\* 0 === 1; // true
```

规范
--

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

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

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

参见
--

* `Number.NaN`
* `Number.isNaN()`
* `isNaN()`


Int32Array - JavaScript

Int32Array
==========

该 **`Int32Array`** 类型表示在平台顺序字节中一个双补码 32 位有符号的整型数组。如果需要控制字节顺序，请改用 `DataView`。此内容的初始化为 `0`。一旦创建，你可以使用对象的方法或者标准数组索引的语法 (即使用括号表示法)。

语法
--
```

new Int32Array(length);
new Int32Array(typedArray);
new Int32Array(object);
new Int32Array(buffer [, byteOffset [, length]]);
```

更多关于构造函数语法和参数的详细信息，请参阅\*TypedArray。\*

属性
--

`Int32Array.BYTES_PER_ELEMENT`

Returns a number value of the element size. `4` in the case of an `Int32Array`.

Int32Array.length

Static length property whose value is 3. For the actual length (number of elements), see `Int32Array.prototype.length`.

`Int32Array.prototype` (en-US)

Prototype for the *TypedArray* objects.

方法
--

`Int32Array.from()`

从类似数组或者可迭代对象中创建一个新的 `Int32Array`。另见 `Array.from()`.

`Int32Array.of()`

创建一个新的具有可变数量参数的 `Int32Array`。另见 `Array.of()`。

`Int32Array` 属性
---------------

所有的`Int32Array`对象都继承自 {jsxref("TypedArray.prototype", "%TypedArray%.prototype")}}。

### 属性

`Int32Array.prototype.constructor`

返回创建实例原型的函数。默认情况下，它是 `Int32Array` 的构造函数。

`Int32Array.prototype.buffer` 只读

返回在构造时被固定的 `Int32Array` 引用的 `ArrayBuffer`，因此只读。

`Int32Array.prototype.byteLength` 只读

返回从其 `ArrayBuffer` 开始的 `Int32Array` 长度的 (以字节为单位)。在构造时被固定，因此只读。

`Int32Array.prototype.byteOffset` 只读

返回从其 `ArrayBuffer` 开始的偏移量 (以字节为单位)。在构造时被固定，因此只读。

`Int32Array.prototype.length` 只读

返回 `Int32ArrayInt32Array` 包含的元素个数。在构造时被固定，因此只读。

### 方法

`Int32Array.prototype.copyWithin()`

Copies a sequence of array elements within the array. See also `Array.prototype.copyWithin()`.

`Int32Array.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()`.

`Int32Array.prototype.every()`

Tests whether all elements in the array pass the test provided by a function. See also `Array.prototype.every()`.

`Int32Array.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()`.

`Int32Array.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()`.

`Int32Array.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()`.

`Int32Array.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()`.

`Int32Array.prototype.forEach()`

Calls a function for each element in the array. See also `Array.prototype.forEach()`.

`Int32Array.prototype.includes()`
实验性

Determines whether a typed array includes a certain element, returning `true` or `false` as appropriate. See also `Array.prototype.includes()`.

`Int32Array.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()`.

`Int32Array.prototype.join()`

Joins all elements of an array into a string. See also `Array.prototype.join()`.

`Int32Array.prototype.keys()`

Returns a new `Array Iterator` that contains the keys for each index in the array. See also `Array.prototype.keys()`.

`Int32Array.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()`.

`Int32Array.prototype.map()`

Creates a new array with the results of calling a provided function on every element in this array. See also `Array.prototype.map()`.

`Int32Array.prototype.move()`
非标准
 未实现

Former non-standard version of `Int32Array.prototype.copyWithin()`.

`Int32Array.prototype.reduce()`

Apply a function against an accumulator and each value of the array (from left-to-right) as to reduce it to a single value. See also `Array.prototype.reduce()`.

`Int32Array.prototype.reduceRight()`

Apply a function against an accumulator and each value of the array (from right-to-left) as to reduce it to a single value. See also `Array.prototype.reduceRight()`.

`Int32Array.prototype.reverse()`

Reverses the order of the elements of an array — the first becomes the last, and the last becomes the first. See also `Array.prototype.reverse()`.

`Int32Array.prototype.set()`

在类型化数组中存储多个值，从指定数组中读取输入值。

`Int32Array.prototype.slice()`

提取数组中的一部分，并返回一个新的数组。更多请见`Array.prototype.slice()`。

`Int32Array.prototype.some()`

如果数组中至少有一个元素满足所提供的测试函数，则返回 true。更多请见`Array.prototype.some()`。

`Int32Array.prototype.sort()`

Sorts the elements of an array in place and returns the array. See also `Array.prototype.sort()`.

`Int32Array.prototype.subarray()`

Returns a new `Int32Array` from the given start and end element index.

`Int32Array.prototype.values()`

Returns a new `Array Iterator` object that contains the values for each index in the array. See also `Array.prototype.values()`.

`Int32Array.prototype.toLocaleString()`

Returns a localized string representing the array and its elements. See also `Array.prototype.toLocaleString()`.

`Int32Array.prototype.toString()`

Returns a string representing the array and its elements. See also `Array.prototype.toString()`.

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

Returns a new `Array Iterator` object that contains the values for each index in the array.

示例
--

不同的方式创建一个 `Int32Array`

```js
// 从一个数字
var int32 = new Int32Array(2);
int32[0] = 42;
console.log(int32[0]); // 42
console.log(int32.length); // 2
console.log(int32.BYTES\_PER\_ELEMENT); // 4

// 从一个数组
var arr = new Int32Array([21, 31]);
console.log(arr[1]); // 31

// 从一个其他 TypedArray
var x = new Int32Array([21, 31]);
var y = new Int32Array(x);
console.log(y[0]); // 21

// 从一个 ArrayBuffer
var buffer = new ArrayBuffer(16);
var z = new Int32Array(buffer, 0, 4);
```

规范
--

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

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

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

兼容性说明
-----

Starting with ECMAScript 2015 (ES6), `In32Array` constructors require to be constructed with a `new` operator. Calling a `Int32Array` constructor as a function without `new`, will throw a `TypeError` from now on.

```js
var dv = Int32Array([1, 2, 3]);
// TypeError: calling a builtin Int32Array constructor
// without new is forbidden
```

```js
var dv = new Int32Array([1, 2, 3]);
```

参见
--

* JavaScript typed arrays
* `ArrayBuffer`
* `DataView`


encodeURI() - JavaScript

encodeURI()
===========

**`encodeURI()`** 函数通过将特定字符的每个实例替换为一个、两个、三或四转义序列来对统一资源标识符 (URI) 进行编码 (该字符的 UTF-8 编码仅为四转义序列) 由两个 "代理" 字符组成)。

语法
--
```

encodeURI(URI)
```

### 参数

`URI`

一个完整的 URI。

### 返回值

一个新字符串，表示提供的字符串编码为统一资源标识符 (URI)。

描述
--

假定一个 URI 是完整的 URI，那么无需对那些保留的并且在 URI 中有特殊意思的字符进行编码。
```

http://username:password@www.example.com:80/path/to/file.php?foo=316&bar=this+has+spaces#anchor
```

`encodeURI` 会替换所有的字符，但不包括以下字符，即使它们具有适当的 UTF-8 转义序列：

| 类型 | 包含 |
| --- | --- |
| 保留字符 | `;` `,` `/` `?` `:` `@` `&` `=` `+` `$` |
| 非转义的字符 | 字母 数字 `-` `_` `.` `!` `~` `*` `'` `(` `)` |
| 数字符号 | `#` |

请注意，`encodeURI` 自身*无法*产生能适用于 HTTP GET 或 POST 请求的 URI，例如对于 XMLHTTPRequests，因为 "&", "+", 和 "=" 不会被编码，然而在 GET 和 POST 请求中它们是特殊字符。然而`encodeURIComponent`这个方法会对这些字符编码。

另外，如果试图编码一个非高 - 低位完整的代理字符，将会抛出一个 `URIError` 错误，例如：

```js
// 编码高 - 低位完整字符 ok
console.log(encodeURI("\uD800\uDFFF"));

// 编码单独的高位字符抛出 "Uncaught URIError: URI malformed"
console.log(encodeURI("\uD800"));

// 编码单独的低位字符抛出 "Uncaught URIError: URI malformed"
console.log(encodeURI("\uDFFF"));
```

并且需要注意，如果 URL 需要遵循较新的RFC3986标准，那么方括号是被保留的 (给 IPv6)，因此对于那些没有被编码的 URL 部分 (例如主机)，可以使用下面的代码：

```js
function fixedEncodeURI(str) {
  return encodeURI(str).replace(/%5B/g, "[").replace(/%5D/g, "]");
}
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-encodeuri-uri |

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

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

参见
--

* `decodeURI`
* `encodeURIComponent`
* `decodeURIComponent`


WeakRef - JavaScript

WeakRef
=======

WeakRef 对象允许你保留对另一个对象的弱引用，而不会阻止被弱引用对象被 GC 回收

描述
--

WeakRef 对象包含对对象的弱引用，这个弱引用被称为该 WeakRef 对象的 target 或者是 referent。对对象的弱引用是指当该对象应该被 GC 回收时不会阻止 GC 的回收行为。而与此相反的，一个普通的引用（默认是强引用）会将与之对应的对象保存在内存中。只有当该对象没有任何的强引用时，JavaScript 引擎 GC 才会销毁该对象并且回收该对象所占的内存空间。如果上述情况发生了，那么你就无法通过任何的弱引用来获取该对象。

**备注：** 在使用前请阅读Avoid where possible，对于 WeakRef 对象的使用要慎重考虑，**能不使用就尽量不要使用**

构造函数
----

`WeakRef()`

创建一个 WeakRef 对象

实例方法
----

`WeakRef.prototype.deref()`

返回当前实例的 WeakRef 对象所绑定的 target 对象，如果该 target 对象已被 GC 回收则返回`undefined`

为什么尽量避免使用
---------

正确使用 WeakRef 对象需要仔细的考虑，最好尽量避免使用。避免依赖于规范没有保证的任何特定行为也是十分重要的。何时、如何以及是否发生垃圾回收取决于任何给定 JavaScript 引擎的实现。**GC 在一个 JavaScript 引擎中的行为有可能在另一个 JavaScript 引擎中的行为大相径庭，或者甚至在同一类引擎，不同版本中 GC 的行为都有可能有较大的差距**。GC 目前还是 JavaScript 引擎实现者不断改进和改进解决方案的一个难题。

以下是 WeakRef 提案的作者在其解释文件（explainer document）中提出的一些具体观点

>
> Garbage collectors are complicated. If an application or library depends on GC cleaning up a WeakRef or calling a finalizer [cleanup callback] in a timely, predictable manner, it's likely to be disappointed: the cleanup may happen much later than expected, or not at all. Sources of variability include:
>
>
> * One object might be garbage-collected much sooner than another object, even if they become unreachable at the same time, e.g., due to generational collection.
> * Garbage collection work can be split up over time using incremental and concurrent techniques.
> * Various runtime heuristics can be used to balance memory usage, responsiveness.
> * The JavaScript engine may hold references to things which look like they are unreachable (e.g., in closures, or inline caches).
> * Different JavaScript engines may do these things differently, or the same engine may change its algorithms across versions.
> * Complex factors may lead to objects being held alive for unexpected amounts of time, such as use with certain APIs.
>
>
>

关于 WeakRefs 的说明
---------------

关于 WeakRefs 的一些说明

* 如果你的代码刚刚为目标对象创建了 WeakRef，或者从 WeakRef 的 deref 方法获取了目标对象，在当前 JavaScript job (包括在脚本作业末尾运行的任何 promise reaction 作业) 结束之前，不会回收该目标对象。也就是说，你只能“看到”在事件循环的两次循环之间回收的对象。这主要是为了避免在代码中显示任何给定 JavaScript 引擎的 GC 行为 ------ 因为如果不这样的话，那么人们会根据这个行为来编写代码，而当 GC 的行为改变时，就会造成不可预知的后果。(GC 是一个棘手的问题；JavaScript 引擎实现者依然不断地改进和改进它的工作方式)
* 如果多个`WeakRef`s 有相同的目标，那么他们的 target 对象是一样的。对其中一个调用 deref 的结果将与对另一个调用 deref 的结果匹配（在同一个作业中），你不会从其中一个获取目标对象，而是从另一个获取未定义的对象。
* 如果一个对象是 WeakRef 的 target，又是 in a `FinalizationRegistry` (en-US),那么该 target 就会在调用与注册表关联的任何清理回调之前或者同时被清理。如果清理回调调用对象的 WeakRef 上的 deref，它将收到`undefined`
* 你不能更改 WeakRef 的 target，它将始终是第一次指定的 target 或者在回收该 target 时会定义
* WeakRef 可能永远不会从 deref 返回 undefined，即使没有什么东西能很好地保存 target，因为 GC 可能永远不会决定回收对象。
示例
--

### 使用 WeakRef 对象

这个例子演示了在一个 DOM 元素中启动一个计数器，当这个元素不存在时停止：

```js
class Counter {
  constructor(element) {
    // Remember a weak reference to the DOM element
    this.ref = new WeakRef(element);
    this.start();
  }

  start() {
    if (this.timer) {
      return;
    }

    this.count = 0;

    const tick = () => {
      // Get the element from the weak reference, if it still exists
      const element = this.ref.deref();
      if (element) {
        element.textContent = ++this.count;
      } else {
        // The element doesn't exist anymore
        console.log("The element is gone.");
        this.stop();
        this.ref = null;
      }
    };

    tick();
    this.timer = setInterval(tick, 1000);
  }

  stop() {
    if (this.timer) {
      clearInterval(this.timer);
      this.timer = 0;
    }
  }
}

const counter = new Counter(document.getElementById("counter"));
counter.start();
setTimeout(() => {
  document.getElementById("counter").remove();
}, 5000);
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-weak-ref-objects |

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

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

参见
--

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


parseFloat - JavaScript

parseFloat
==========

**`parseFloat()`** 函数解析一个参数（必要时先转换为字符串）并返回一个浮点数。

尝试一下
----

语法
--

```js
parseFloat(string)
```

### 参数

`string`

需要被解析成为浮点数的值。

### 返回值

给定值被解析成浮点数。如果给定值不能被转换成数值，则会返回 `NaN`。

描述
--

`parseFloat` 是个全局函数，不属于任何对象。

* 如果 `parseFloat` 在解析过程中遇到了正号（`+`）、负号（`-` U+002D HYPHEN-MINUS）、数字（`0`-`9`）、小数点（`.`）、或者科学记数法中的指数（e 或 E）以外的字符，则它会忽略该字符以及之后的所有字符，返回当前已经解析到的浮点数。
* 第二个小数点的出现也会使解析停止（在这之前的字符都会被解析）。
* 参数首位和末位的空白符会被忽略。
* 如果参数字符串的第一个字符不能被解析成为数字，则 `parseFloat` 返回 `NaN`。
* `parseFloat` 也可以解析并返回 `Infinity`。
* `parseFloat` 解析 `BigInt` 为 `Numbers`, 丢失精度。因为末位 `n` 字符被丢弃。

考虑使用 `Number(*value*)` 进行更严谨的解析，只要参数带有无效字符就会被转换为 `NaN` 。

`parseFloat` 也可以转换一个已经定义了 `toString` 或者 `valueOf` 方法的对象，它返回的值和在调用该方法的结果上调用 `parseFloat` 值相同。

示例
--

### 示例：`parseFloat`返回正常数字

下面的例子都返回 **3.14**

```js
parseFloat(3.14);
parseFloat("3.14");
parseFloat(" 3.14 ");
parseFloat("314e-2");
parseFloat("0.0314E+2");
parseFloat("3.14some non-digit characters");
parseFloat({
  toString: function () {
    return "3.14";
  },
});
```

### `parseFloat` 返回 NaN

下面的例子将返回`NaN`

```js
parseFloat("FF2");
```

### parseFloat 和 BigInt

以下例子均返回 `900719925474099300`，当整数太大以至于不能被转换时将失去精度。

```js
parseFloat(900719925474099267n);
parseFloat("900719925474099267n");
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-parsefloat-string |

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

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

参见
--

* `parseInt()`
* `Number.parseFloat()`
* `Number.parseInt()`
* `isNaN()`


Float32Array - JavaScript

Float32Array
============

**`Float32Array`** 类型数组代表的是平台字节顺序为 32 位的浮点数型数组 (对应于 C 浮点数据类型) 。如果需要控制字节顺序，使用 `DataView` 替代。其内容初始化为 `0`。一旦建立起来，你可以使用这个对象的方法对其元素进行操作，或者使用标准数组索引语法 (使用方括号)。

语法
--
```

new Float32Array(length);
new Float32Array(typedArray);
new Float32Array(object);
new Float32Array(buffer [, byteOffset [, length]]);
```

更多的语法信息和参数，参见 *TypedArray。*

属性
--

`Float32Array.BYTES_PER_ELEMENT`

返回元素字节数。在 `Float32Array` 的情况下返回 4。

Float32Array.length

长度属性的值为 3。关于其实际长度 (元素数量) 参见`Float32Array.prototype.length`。

`Float32Array.prototype` (en-US)

*TypedArray* 对象的原型。

方法
--

`Float32Array.from()`

从一个类数组对象或可遍历对象创建一个新的 Float32Array。参见 `Array.from()`。

`Float32Array.of()`

用可变数量的参数创建一个新的 Float32Array。参见 `Array.of()`。

`Float32Array` 属性
-----------------

所有的`Float32Array 对象都`继承自 `%TypedArray%.prototype` (en-US)。

### 特性

`Float32Array.prototype.constructor`

返回创建这个实例原型的函数。这是 `Float32Array` 默认的构造函数。

`Float32Array.prototype.buffer` 只读

返回这个`Float32Array` 引用的 `ArrayBuffer`。构造时已固定，所以是**只读**的。

`Float32Array.prototype.byteLength` 只读

返回从`Float32Array` 的 `ArrayBuffer` 开头开始的长度 (以字节为单位) 。构造时已固定，所以是**只读**的。

`Float32Array.prototype.byteOffset` 只读

返回从`Float32Array` 的 `ArrayBuffer` 开头开始的偏移量（以字节为单位）。构造时已固定，所以是**只读**的。

`Float32Array.prototype.length` 只读

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

### 方法

`Float32Array.prototype.copyWithin()`

从数组复制元素。参见`Array.prototype.copyWithin()`。

`Float32Array.prototype.entries()`

`返回一个包含数组中每个元素键值对的数组遍历器对象`。参见`Array.prototype.entries()`。

`Float32Array.prototype.every()`

检测是否所有元素都能通过给定函数的测试。参见`Array.prototype.every()`。

`Float32Array.prototype.fill()`

用一个静态值填充给定的起始位置。参见`Array.prototype.fill()`。

`Float32Array.prototype.filter()`

创建一个新数组，数据为原数组中所有能让给入函数返回 true 的元素。参见`Array.prototype.filter()`。

`Float32Array.prototype.find()`

返回满足测试函数的值，如果没有找到，返回 undefined。参见`Array.prototype.find()`。

`Float32Array.prototype.findIndex()`

返回满足测试函数的值的位置，如果没有找到，返回 -1。参见`Array.prototype.findIndex()`。

`Float32Array.prototype.forEach()`

以每个元素为参数各调用一次函数。参见`Array.prototype.forEach()`。

`Float32Array.prototype.includes()`
实验性

判断是否包含某个元素，返回 `true` 或 `false`。参见`Array.prototype.includes()`。

`Float32Array.prototype.indexOf()`

返回数组中等于给定值的元素的第一个（最小）位置，没有找到则返回 -1。参见`Array.prototype.indexOf()`。

`Float32Array.prototype.join()`

合并所有数组元素到一个字符串中。参见`Array.prototype.join()`。

`Float32Array.prototype.keys()`

返回一个包含数组中所有索引的数组遍历器。参见`Array.prototype.keys()`。

`Float32Array.prototype.lastIndexOf()`

返回数组中等于给定值的元素的最后（最大）位置，没有找到则返回 -1。参见`Array.prototype.lastIndexOf()`。

`Float32Array.prototype.map()`

创建一个新的数组，数据由原数组每个元素依次传入给定函数后返回的值组成。参见`Array.prototype.map()`。

`Float32Array.prototype.move()`
非标准
 未实现

`Float32Array.prototype.copyWithin()`以前的一个非标准版本。

`Float32Array.prototype.reduce()`

传入一个函数作为累加器，从左到右遍历，最终得到一个值。参见`Array.prototype.reduce()`。

`Float32Array.prototype.reduceRight()`

传入一个函数作为累加器，从右到左遍历，最终得到一个值。参见`Array.prototype.reduceRight()`。

`Float32Array.prototype.reverse()`

反转数组元素的顺序 — 第一个变为最后一个，最后一个变为第一个。参见`Array.prototype.reverse()`。

`Float32Array.prototype.set()`

从给定的数组存入多个数值。

`Float32Array.prototype.slice()`

提取数组的一部分并且返回一个新数组。参见`Array.prototype.slice()`。

`Float32Array.prototype.some()`

如果数组中至少有一个元素满足测试函数的要求则返回 true。参见`Array.prototype.some()`。

`Float32Array.prototype.sort()`

对数组元素进行排序并返回数组。参见`Array.prototype.sort()`。

`Float32Array.prototype.subarray()`

从给定的起始位置返回一个新的`Float32Array` 。

`Float32Array.prototype.values()`

返回一个包含所有数组元素的数组遍历器对象。参见`Array.prototype.values()`。

`Float32Array.prototype.toLocaleString()`

返回一个代表数组和其元素的本地化格式字符串。参见`Array.prototype.toLocaleString()`。

`Float32Array.prototype.toString()`

返回一个代表数组和它的元素的字符串。参见`Array.prototype.toString()`。

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

返回一个新的包含数组元素的数组迭代器对象。

示例
--

```js
// From a length
var float32 = new Float32Array(2);
float32[0] = 42;
console.log(float32[0]); // 42
console.log(float32.length); // 2
console.log(float32.BYTES\_PER\_ELEMENT); // 4

// From an array
var arr = new Float32Array([21, 31]);
console.log(arr[1]); // 31

// From another TypedArray
var x = new Float32Array([21, 31]);
var y = new Float32Array(x);
console.log(y[0]); // 21

// From an ArrayBuffer
var buffer = new ArrayBuffer(16);
var z = new Float32Array(buffer, 0, 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) 开始， `Float32Array`构造函数需要用一个`new`操作符来构造。现在直接把`Float32Array 构造函数当函数调用而不使用 new，会抛出一个``TypeError`。

```js
var dv = Float32Array([1, 2, 3]);
// TypeError: calling a builtin Float32Array constructor
// 不允许不使用 new
```

```js
var dv = new Float32Array([1, 2, 3]);
```

参见
--

* JavaScript typed arrays
* `ArrayBuffer`
* `DataView`


decodeURIComponent() - JavaScript

decodeURIComponent()
====================

**`decodeURIComponent()`** 方法用于解码由 `encodeURIComponent` 方法或者其他类似方法编码的部分统一资源标识符（URI）。

语法
--
```

decodeURIComponent(encodedURI)
```

### 参数

`encodedURI`

编码后的部分 URI

### 返回值

一个解码后的统一资源标识符（URI）字符串，处理前的 URI 经过了给定格式的编码。

### 异常

当该方法使用不当时，将会抛出一个`URIError`（“格式错误的 URI 序列”）异常。

描述
--

将已编码 URI 中所有能识别的转义序列转换成原字符。

示例
--

### 解码一个西里尔字母的 URL

```js
decodeURIComponent("JavaScript\_%D1%88%D0%B5%D0%BB%D0%BB%D1%8B");
// "JavaScript\_шеллы"
```

### 捕捉异常

```js
try {
  var a = decodeURIComponent("%E0%A4%A");
} catch (e) {
  console.error(e);
}

// URIError: malformed URI sequence
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-decodeuricomponent-encodeduricomponent |

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

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

参见
--

* `decodeURI`
* `encodeURI`
* `encodeURIComponent`


AsyncFunction - JavaScript

AsyncFunction
=============

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

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

* Learn more
* See full compatibility
* Report feedback
**`AsyncFunction`** 对象为异步函数提供方法。在 JavaScript 中，每个异步函数实际上都是一个 `AsyncFunction` 对象。

注意，`AsyncFunction` *不是*全局对象。它可以通过以下代码获取：

```js
const AsyncFunction = async function () {}.constructor;
```

`AsyncFunction` 是 `Function` 的子类。

构造函数
----

`AsyncFunction()`

创建一个新的 `AsyncFunction` 对象。

实例属性
----

*同时也从它的父类 `Function` 继承实例属性*。

这些属性定义在 `AsyncFunction.prototype` 并且由所有 `AsyncFunction` 实例共享。

`AsyncFunction.prototype.constructor`

用于创建实例对象的构造函数。对于 `AsyncFunction` 实例，初始值是 `AsyncFunction` 构造函数。

`AsyncFunction.prototype[@@toStringTag]`

`@@toStringTag` 属性的初始值是字符串 `"AsyncFunction"`。该属性在 `Object.prototype.toString()` 中使用。

**备注：** `AsyncFunction` 实例并没有 `prototype` 属性。

实例方法
----

*同时也从它的父类 `Function` 继承方法*。

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-async-function-objects |

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

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

参见
--

* `async function` 声明
* `async function` 表达式
* `Function`
* `AsyncGeneratorFunction`
* `GeneratorFunction`
* 函数


AggregateError - JavaScript

AggregateError
==============

**`AggregateError`** 对象代表了包装了多个错误对象的单个错误对象。当一个操作需要报告多个错误时，例如 `Promise.any()`，当传递给它的所有承诺都被拒绝时，就会抛出该错误。

`AggregateError` 是 `Error` 的子类。

构造函数
----

`AggregateError()`

创建一个新的 `AggregateError` 对象

实例属性
----

*从父类 `Error` 中继承实例属性。*

以下属性在 `AggregateError.prototype` 上定义，并由所有 `AggregateError` 实例共享。

`AggregateError.prototype.constructor`

创建实例对象的构造函数。对于 `AggregateError` 实例来说，初始值为 `AggregateError` 构造函数。

`AggregateError.prototype.name`

代表了错误类型的名称，对于 `AggregateError.prototype.name` 来说，初始值为 `"AggregateError"`。

这些属性是每个 `AggregateError` 实例的自有属性。

`errors`

一个数组，基本上反映了 `AggregateError` 实例化时使用的迭代器；例如，如果 `AggregateError` 是用 `AggregateError()` 构造函数创建的，则作为第一个参数传递给构造函数的任何迭代器生成的数组。

实例方法
----

*从父类 `Error` 中继承实例方法。*

示例
--

### 捕获一个 AggregateError

```js
Promise.any([Promise.reject(new Error("some error"))]).catch((e) => {
  console.log(e instanceof AggregateError); // true
  console.log(e.message); // "All Promises rejected"
  console.log(e.name); // "AggregateError"
  console.log(e.errors); // [ Error: "some error" ]
});
```

### 创建一个 AggregateError

```js
try {
  throw new AggregateError([new Error("some error")], "Hello");
} catch (e) {
  console.log(e instanceof AggregateError); // true
  console.log(e.message); // "Hello"
  console.log(e.name); // "AggregateError"
  console.log(e.errors); // [ Error: "some error" ]
}
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-aggregate-error-objects |

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

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

参见
--

* `core-js` 中 `AggregateError` 的 polyfill
* `Error`
* `Promise.any`


undefined - JavaScript

undefined
=========

全局属性 **`undefined`** 表示原始值 `undefined`。它是一个 JavaScript 的 原始数据类型 。

| `undefined` 的属性特性 |
| --- |
| 可写 | 否 |
| 可枚举 | 否 |
| 可配置 | 否 |

尝试一下
----

语法
--

```js
undefined
```

描述
--

`undefined`是*全局对象*的一个属性。也就是说，它是全局作用域的一个变量。`undefined`的最初值就是原始数据类型`undefined`。

在现代浏览器（JavaScript 1.8.5/Firefox 4+），自 ECMAscript5 标准以来 undefined 是一个不能被配置（non-configurable），不能被重写（non-writable）的属性。即便事实并非如此，也要避免去重写它。

一个没有被赋值的变量的类型是 undefined。如果方法或者是语句中**操作的变量没有被赋值，则会返回 undefined**（对于这句话持疑惑态度，请查看英文原文来理解）。

```js
function test(a) {
  console.log(typeof a); // undefined
  return a;
}

test(); // 返回"undefined"
```

一个函数如果没有使用 return 语句指定`返回`值，就会返回一个 undefined 值。

**警告：** 但是它有可能在非全局作用域中被当作标识符（变量名）来使用（因为 undefined 不是保留字），这样做是一个非常坏的主意，因为这样会使你的代码难以去维护和排错。

```js
// 不要这样做！

(() => {
  const undefined = "foo";
  console.log(undefined, typeof undefined); // foo string
})();

((undefined) => {
  console.log(undefined, typeof undefined); // foo string
})("foo");
```

示例
--

### 严格相等和 undefined

你可以使用 undefined 和严格相等或不相等操作符来决定一个变量是否拥有值。在下面的代码中，变量 x 是未定义的，if 语句的求值结果将是 true

```js
var x;

if (x === undefined) {
  // 执行这些语句
} else {
  // 这些语句不会被执行
}
```

**备注：** 这里是必须使用严格相等操作符（===）而不是标准相等操作符（==），因为 x == undefined 会检查 x 是不是 null，但是严格相等不会检查（有点饶人，其实 === 会严格判断双方的类型、值等是否相等）。null 不等同于 undefined。移步`比较操作符` (en-US)查看详情。

### Typeof 操作符和 undefined

或者，可以使用`typeof`：

```js
var x;
if (typeof x === "undefined") {
  // 执行这些语句
}
```

使用 `typeof`的原因是它不会在一个变量没有被声明的时候抛出一个错误。

```js
// 这里没有声明 y
if (typeof y === "undefined") {
  // 没有错误，执行结果为 true
  console.log("y is " + typeof y); // y is undefined
}

if (y === undefined) {
  // ReferenceError: y is not defined
}
```

但是，技术方面看来这样的使用方法应该被避免。JavaScript 是一个静态作用域语言，所以，一个变量是否被声明可以通过看它是否在一个封闭的上下文中被声明。唯一的例外是全局作用域，但是全局作用域是被绑定在全局对象上的，所以要检查一个变量是否在全局上下文中存在可以通过检查全局对象上是否存在这个属性（比如使用`in`操作符）。

```js
if ("x" in window) {
  // 只有 x 被全局性的定义 才会执行这些语句
}
```

### Void 操作符和 undefined

`void` 操作符是第三种可以替代的方法。

```js
var x;
if (x === void 0) {
  // 执行这些语句
}

// 没有声明 y
if (y === void 0) {
  // 抛出一个 RenferenceError 错误（与 `typeof` 相比）
}
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-undefined |

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

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