Array.prototype.keys() - JavaScript

Array.prototype.keys()
======================

**`keys()`** 方法返回一个新的*数组迭代器*对象，其中包含数组中每个索引的键。

尝试一下
----

语法
--

```js
keys()
```

### 返回值

一个新的可迭代迭代器对象。

描述
--

当用于稀疏数组时，`keys()` 方法迭代空槽，就像它们的值为 `undefined` 一样。

`keys()` 方法是通用的。它只期望 `this` 值具有 `length` 属性和整数键属性。

示例
--

### 在稀疏数组中使用 keys()

与 `Object.keys()` 只包含数组中实际存在的键不同，`keys()` 迭代器不会忽略缺失属性的键。

```js
const arr = ["a", , "c"];
const sparseKeys = Object.keys(arr);
const denseKeys = [...arr.keys()];
console.log(sparseKeys); // ['0', '2']
console.log(denseKeys); // [0, 1, 2]
```

### 在非数组对象上调用 keys()

`keys()` 方法读取 `this` 的 `length` 属性，然后生成 0 到 `length - 1` 之间的所有整数索引。实际并不会访问索引。

```js
const arrayLike = {
  length: 3,
};
for (const entry of Array.prototype.keys.call(arrayLike)) {
  console.log(entry);
}
// 0
// 1
// 2
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-array.prototype.keys |

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

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

参见
--

* `core-js` 中 `Array.prototype.keys` 的 polyfill
* 索引集合类
* `Array`
* `Array.prototype.entries()`
* `Array.prototype.values()`
* `Array.prototype[@@iterator]()`
* `TypedArray.prototype.keys()`
* 迭代协议


Array.prototype.every() - JavaScript

Array.prototype.every()
=======================

**`every()`** 方法测试一个数组内的所有元素是否都能通过指定函数的测试。它返回一个布尔值。

尝试一下
----

语法
--

```js
every(callbackFn)
every(callbackFn, thisArg)
```

### 参数

`callbackFn`

为数组中的每个元素执行的函数。它应该返回一个真值以指示元素通过测试，否则返回一个假值。该函数被调用时将传入以下参数：

`element`

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

`index`

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

`array`

调用了 `every()` 的数组本身。

`thisArg` 可选

执行 `callbackFn` 时用作 `this` 的值。参见迭代方法。

### 返回值

如果 `callbackFn` 为每个数组元素返回真值，则为 `true`。否则为 `false`。

描述
--

`every()` 方法是一个迭代方法。它为数组中的每个元素调用一次指定的 `callbackFn` 函数，直到 `callbackFn` 返回一个假值。如果找到这样的元素，`every()` 方法将会立即返回 `false` 并停止遍历数组。否则，如果 `callbackFn` 为每个元素返回一个真值，`every()` 就会返回 `true`。

`every` 和数学中的全称量词"任意（∀）"类似。特别的，对于空数组，它只返回 `true`。（这种情况属于无条件正确，因为空集的所有元素都符合给定的条件。）

`callbackFn` 仅针对已分配值的数组索引调用。它不会为稀疏数组中的空槽调用。

`every()` 不会改变调用它的数组，但指定的 `callbackFn` 函数可以。但是请注意，数组的长度是在第一次调用 `callbackFn` *之前*保存的。所以：

* 当开始调用 `every()` 时，`callbackFn` 将不会访问超出数组初始长度的任何元素。
* 对已访问索引的更改不会导致再次在这些元素上调用 `callbackFn`。
* 如果数组中一个现有的、尚未访问的元素被 `callbackFn` 更改，则它传递给 `callbackFn` 的值将是该元素被修改后的值。被删除的元素则不会被访问。

**警告：** 上述类型的并发修改经常导致难以理解的代码，通常应避免（特殊情况除外）。

`every()` 方法是通用的。它只期望 `this` 值具有 `length` 属性和整数键属性。

示例
--

### 检查所有数组元素的大小

下例测试数组中的所有元素是否都大于 10。

```js
function isBigEnough(element, index, array) {
  return element >= 10;
}
[12, 5, 8, 130, 44].every(isBigEnough); // false
[12, 54, 18, 130, 44].every(isBigEnough); // true
```

### 检查一个数组是否是另一个数组的子集

下面的示例测试一个数组的所有元素是否都存在于另一个数组中。

```js
const isSubset = (array1, array2) =>
  array2.every((element) => array1.includes(element));

console.log(isSubset([1, 2, 3, 4, 5, 6, 7], [5, 7, 6])); // true
console.log(isSubset([1, 2, 3, 4, 5, 6, 7], [5, 8, 7])); // false
```

### 在稀疏数组上使用 every()

`every()` 不会在空槽上运行它的断言函数。

```js
console.log([1, , 3].every((x) => x !== undefined)); // true
console.log([2, , 2].every((x) => x === 2)); // true
```

### 影响初始数组（修改、添加和删除）

以下示例测试修改数组时 `every` 方法的行为。

```js
// --------------
// 修 改 元 素
// --------------
let arr = [1, 2, 3, 4];
arr.every((elem, index, arr) => {
  arr[index + 1]--;
  console.log(`[${arr}][${index}] -> ${elem}`);
  return elem < 2;
});

// 循环会迭代 3 次
// 没有修改的情况下只会迭代 2 次
//
// 第 1 次迭代：[1,1,3,4][0] -> 1
// 第 2 次迭代：[1,1,2,4][1] -> 1
// 第 3 次迭代：[1,1,2,3][2] -> 2

// --------------
// 添 加 元 素
// --------------
arr = [1, 2, 3];
arr.every((elem, index, arr) => {
  arr.push("new");
  console.log(`[${arr}][${index}] -> ${elem}`);
  return elem < 4;
});

// 循环迭代 3 次，即使在添加新元素之后
//
// 第 1 次迭代：[1, 2, 3, new][0] -> 1
// 第 2 次迭代：[1, 2, 3, new, new][1] -> 2
// 第 3 次迭代：[1, 2, 3, new, new, new][2] -> 3

// --------------
// 删 除 元 素
// --------------
arr = [1, 2, 3, 4];
arr.every((elem, index, arr) => {
  arr.pop();
  console.log(`[${arr}][${index}] -> ${elem}`);
  return elem < 4;
});

// 循环仅迭代 2 次
// 因为剩余的元素被 `pop()` 删除
//
// 第 1 次迭代：[1,2,3][0] -> 1
// 第 2 次迭代：[1,2][1] -> 2
```

### 在非数组对象上调用 every()

`every()` 方法读取 `this` 的 `length` 属性，然后访问每个整数索引，直到到达末尾或 `callbackFn` 返回 `false`。

```js
const arrayLike = {
  length: 3,
  0: "a",
  1: "b",
  2: "c",
};
console.log(
  Array.prototype.every.call(arrayLike, (x) => typeof x === "string"),
); // true
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-array.prototype.every |

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

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

参见
--

* `core-js` 中 `Array.prototype.every` 的 polyfill
* 索引集合类
* `Array`
* `Array.prototype.forEach()`
* `Array.prototype.some()`
* `Array.prototype.find()`
* `TypedArray.prototype.every()`


Array.prototype.map() - JavaScript

Array.prototype.map()
=====================

**`map()`** 方法**创建一个新数组**，这个新数组由原数组中的每个元素都调用一次提供的函数后的返回值组成。

尝试一下
----

语法
--

```js
map(callbackFn)
map(callbackFn, thisArg)
```

### 参数

`callbackFn`

为数组中的每个元素执行的函数。它的返回值作为一个元素被添加为新数组中。该函数被调用时将传入以下参数：

`element`

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

`index`

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

`array`

调用了 `map()` 的数组本身。

`thisArg` 可选

执行 `callbackFn` 时用作 `this` 的值。参见迭代方法。

### 返回值

一个新数组，每个元素都是回调函数的返回值。

描述
--

`map()` 方法是一个迭代方法。它为数组中的每个元素调用一次提供的 `callbackFn` 函数，并用结果构建一个新数组。

`callbackFn` 仅在已分配值的数组索引处被调用。它不会在稀疏数组中的空槽处被调用。

`map()` 方法是一个复制方法。它不会改变 `this`。然而，作为 `callbackFn` 提供的函数可以更改数组。请注意，在第一次调用 `callbackFn` *之前*，数组的长度已经被保存。因此：

* 当开始调用 `map()` 时，`callbackFn` 将不会访问超出数组初始长度的任何元素。
* 对已访问索引的更改不会导致再次在这些元素上调用 `callbackFn`。
* 如果数组中一个现有的、尚未访问的元素被 `callbackFn` 更改，则它传递给 `callbackFn` 的值将是该元素被修改后的值。被删除的元素则不会被访问。

**警告：** 上述类型的并发修改经常导致难以理解的代码，通常应避免（特殊情况除外）。

`map()` 方法是通用的。它只期望 `this` 值具有 `length` 属性和整数键属性。

由于 `map` 创建一个新数组，在没有使用返回的数组的情况下调用它是不恰当的；应该使用 `forEach` 或 `for...of` 作为代替。

示例
--

### 求数组中每个元素的平方根

下面的代码创建了一个新数组，值为原数组中对应数字的平方根。

```js
const numbers = [1, 4, 9];
const roots = numbers.map((num) => Math.sqrt(num));

// roots 现在是 [1, 2, 3]
// numbers 依旧是 [1, 4, 9]
```

### 使用 map 重新格式化数组中的对象

以下代码使用一个包含对象的数组来重新创建一个格式化后的数组。

```js
const kvArray = [
  { key: 1, value: 10 },
  { key: 2, value: 20 },
  { key: 3, value: 30 },
];

const reformattedArray = kvArray.map(({ key, value }) => ({ [key]: value }));

console.log(reformattedArray); // [{ 1: 10 }, { 2: 20 }, { 3: 30 }]
console.log(kvArray);
// [
// { key: 1, value: 10 },
// { key: 2, value: 20 },
// { key: 3, value: 30 }
// ]
```

### 使用包含单个参数的函数来映射一个数字数组

下面的代码表示了当函数需要一个参数时 `map` 的工作方式。当 `map` 循环遍历原始数组时，这个参数会自动被分配成数组中对应的每个元素。

```js
const numbers = [1, 4, 9];
const doubles = numbers.map((num) => num \* 2);

console.log(doubles); // [2, 8, 18]
console.log(numbers); // [1, 4, 9]
```

### 在非数组对象上调用 map()

`map()` 方法读取 `this` 的 `length` 属性，然后访问每个整数索引。

```js
const arrayLike = {
  length: 3,
  0: 2,
  1: 3,
  2: 4,
};
console.log(Array.prototype.map.call(arrayLike, (x) => x \*\* 2));
// [ 4, 9, 16 ]
```

### 在 NodeList 上通用地使用 map()

以下示例展示了如何去遍历通过 `querySelectorAll` 得到的对象集合。这是因为 `querySelectorAll` 返回的是一个对象集合 `NodeList`。

在这种情况下，我们返回屏幕上所有选中 `option` 的值：

```js
const elems = document.querySelectorAll("select option:checked");
const values = Array.prototype.map.call(elems, ({ value }) => value);
```

一种更简单的方式是用 `Array.from()` 方法。

### 在稀疏数组上使用 map()

稀疏数组在使用 `map()` 方法后仍然是稀疏的。空槽的索引在返回的数组中仍然为空，并且回调函数不会对它们进行调用。

```js
console.log(
  [1, , 3].map((x, index) => {
    console.log(`Visit ${index}`);
    return x \* 2;
  }),
);
// Visit 0
// Visit 2
// [2, empty, 6]
```

### 将 parseInt() 与 map() 一起使用

（受到这篇博文的启发）

通常情况下，我们使用只有一个参数（即正在遍历的元素）的回调函数。尽管某些函数可以接受额外的可选参数，但在实际应用中，我们通常只传递一个参数。这种习惯可能会导致一些令人困惑的现象。

考虑下例：

```js
["1", "2", "3"].map(parseInt);
```

我们期望输出 `[1, 2, 3]`, 而实际结果是 `[1, NaN, NaN]`.

`parseInt` 函数通常只使用一个参数，但其实可以传入两个参数。第一个参数是表达式，第二个参数是解析该表达式的基数。当在 `Array.prototype.map` 的回调函数中使用 `parseInt` 函数时，`map` 方法会传递 3 个参数：

* 元素
* 索引
* 数组

`parseInt` 函数会忽略第三个参数，但是*不会忽略*第二个参数！这可能会导致一些问题。

以下是迭代步骤的简明示例：

```js
// parseInt(string, radix) -> map(parseInt(value, index))
/\* 第一次迭代 (index 是 0): \*/ parseInt("1", 0); // 1
/\* 第二次迭代 (index 是 1): \*/ parseInt("2", 1); // NaN
/\* 第三次迭代 (index 是 2): \*/ parseInt("3", 2); // NaN
```

下面让我们来讨论解决方案：

```js
const returnInt = (element) => parseInt(element, 10);

["1", "2", "3"].map(returnInt); // [1, 2, 3]
// 实际结果是一个数字数组（如预期）

// 与上面相同，但使用简洁的箭头函数语法
["1", "2", "3"].map((str) => parseInt(str)); // [1, 2, 3]

// 实现上述目标更简单的方法，同时避免了“骗招”：
["1", "2", "3"].map(Number); // [1, 2, 3]

// 但与 parseInt() 不同，Number() 还会返回一个浮点数或（解析）指数表示法：
["1.1", "2.2e2", "3e300"].map(Number); // [1.1, 220, 3e+300]

// 为了进行比较，如果我们对上面的数组使用 parseInt():
["1.1", "2.2e2", "3e300"].map((str) => parseInt(str)); // [1, 2, 3]
```

使用 `parseInt` 作为参数调用 map 方法，输出运行如下：

```js
const strings = ["10", "10", "10"];
const numbers = strings.map(parseInt);

console.log(numbers);
// 根据以上描述，[10, NaN, 2] 的实际结果可能会出乎意料。
```

### 映射包含 undefined 的数组

当返回 `undefined` 或没有返回任何内容时：

```js
const numbers = [1, 2, 3, 4];
const filteredNumbers = numbers.map((num, index) => {
  if (index < 3) {
    return num;
  }
});

// index 从 0 开始，因此 filterNumbers 为 1、2、3 和 undefined。
// filteredNumbers 是 [1, 2, 3, undefined]
// numbers 依旧是 [1, 2, 3, 4]
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-array.prototype.map |

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

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

参见
--

* `core-js` 中 `Array.prototype.map` 的 polyfill
* 索引集合类
* `Array`
* `Array.prototype.forEach()`
* `Array.from()`
* `TypedArray.prototype.map()`
* `Map`


Array.prototype.findLast() - JavaScript

Array.prototype.findLast()
==========================

**`findLast()`** 方法反向迭代数组，并返回满足提供的测试函数的第一个元素的值。如果没有找到对应元素，则返回 `undefined`。

如果你需要找到：

* *第一个*匹配的元素，使用 `find()`。
* 数组中最后一个匹配元素的*索引*，使用 `findLastIndex()`。
* *某个值的索引*，使用 `indexOf()`。（它类似于 `findIndex()`，但是会检查每个元素是否与值相等，而不是使用一个测试函数。）
* 该数组中是否*存在*一个值，使用 `includes()`。同样地，它检查每个元素是否和值相等，而不是使用一个测试函数。
* 是否有任意一个元素满足提供的测试函数，使用 `some()`。
尝试一下
----

语法
--

```js
findLast(callbackFn)
findLast(callbackFn, thisArg)
```

### 参数

`callbackFn`

数组中测试元素的函数。回调应该返回一个真值，表示已找到匹配的元素，否则返回一个假值。函数在被调用时会传递以下参数：

`element`

当前遍历到的元素。

`index`

当前遍历到的元素的索引（位置）。

`array`

调用 `findLast()` 的数组本身。

`thisArg` 可选

执行 `callbackFn` 时，用作 `this` 的值。详见迭代方法。

### 返回值

数组中满足提供的测试函数索引最高的元素；如果没有元素匹配，返回 `undefined`。

描述
--

`findLast()` 是一个迭代方法。该方法对数组每一个元素按降序（索引从大到小）执行 `callbackFn` 函数，直到 `callbackFn` 返回一个真值。然后 `findLast()` 返回该元素的值并停止遍历数组。如果 `callbackFn` 没有返回一个真值，则 `findLast()` 返回 `undefined`。

`callbackFn` 会被数组中的*每个*元素调用，而不仅仅是那些被赋值的元素。对于稀疏数组来说，空槽行为和 `undefined` 相同。

`findLast()` 方法不会改变调用它的数组，但是提供的 `callbackFn` 可以。但是请注意，数组的长度是在第一次调用 `callbackFn` *之前*保存的。因此：

* `callbackFn` 不会访问在调用 `findLast()` 开始后才添加到数组中的任何元素。
* 给已访问过的索引重新赋值将不会被 `callbackFn` 重新访问。
* 如果 `callbackFn` 更改了数组中现有的、尚未访问的元素，则其传递给 `callbackFn` 的值将是 `findLast()` 访问该元素索引时的值。已删除的元素会被当做 `undefined` 来访问。

**警告：** 上一段描述的并发修改的情况经常导致难以理解的代码，通常应该避免（特殊情况除外）。

`findLast()` 方法是通用的。它只期望 `this` 值具有 `length` 属性和整数键的属性。

示例
--

### 查找与元素属性匹配的数组中的最后一个对象

此示例展示了如何根据数组元素的属性创建测试。

```js
const inventory = [
  { name: "apples", quantity: 2 },
  { name: "bananas", quantity: 0 },
  { name: "fish", quantity: 1 },
  { name: "cherries", quantity: 5 },
];

// 库存低时返回 true
function isNotEnough(item) {
  return item.quantity < 2;
}

console.log(inventory.findLast(isNotEnough));
// { name: "fish", quantity: 1 }
```

#### 使用箭头函数和解构

前面的示例可以使用箭头函数和对象解构重写如下：

```js
const inventory = [
  { name: "apples", quantity: 2 },
  { name: "bananas", quantity: 0 },
  { name: "fish", quantity: 1 },
  { name: "cherries", quantity: 5 },
];

const result = inventory.findLast(({ quantity }) => quantity < 2);

console.log(result);
// { name: "fish", quantity: 1 }
```

### 查找数组中的最后一个素数

以下示例查找数组中的最后一个素数元素（如果没有素数，则返回 `undefined`）：

```js
function isPrime(element) {
  if (element % 2 === 0 || element < 2) {
    return false;
  }
  for (let factor = 3; factor <= Math.sqrt(element); factor += 2) {
    if (element % factor === 0) {
      return false;
    }
  }
  return true;
}

console.log([4, 6, 8, 12].findLast(isPrime)); // undefined，没有找到
console.log([4, 5, 7, 8, 9, 11, 12].findLast(isPrime)); // 11
```

### 在稀疏数组上使用 findLast()

稀疏数组中的空槽*被*访问，并被视为 `undefined`。

```js
// 声明一个在索引 2、3 和 4 处没有元素的数组
const array = [0, 1, , , , 5, 6];

// 显示所有的索引（不只包括那些被赋值的）
array.findLast((value, index) => {
  console.log(`访问索引 ${index}，值为 ${value}`);
});
// 访问索引 6，值为 6
// 访问索引 5，值为 5
// 访问索引 4，值为 undefined
// 访问索引 3，值为 undefined
// 访问索引 2，值为 undefined
// 访问索引 1，值为 1
// 访问索引 0，值为 0

// 显示所有的索引（包括已被删除的）
array.findLast((value, index) => {
  // 在第一次迭代时删除值为 5 的元素
  if (index === 6) {
    console.log(`删除值为 array[5]，其值为 ${array[5]}`);
    delete array[5];
  }
  // 元素 5 在被删除后，仍会被访问
  console.log(`访问索引 ${index}，值为 ${value}`);
});
// 删除值为 array[5]，其值为 5
// 访问索引 6，值为 6
// 访问索引 5，值为 undefined
// 访问索引 4，值为 undefined
// 访问索引 3，值为 undefined
// 访问索引 2，值为 undefined
// 访问索引 1，值为 1
// 访问索引 0，值为 0
```

### 在非数组对象上调用 findLast()

`findLast()` 方法读取 `this` 的 `length` 属性，然后访问每个整数索引。

```js
const arrayLike = {
  length: 3,
  0: 2,
  1: 7.3,
  2: 4,
};
console.log(
  Array.prototype.findLast.call(arrayLike, (x) => Number.isInteger(x)),
); // 4
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-array.prototype.findlast |

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

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

参见
--

* `core-js` 中 `Array.prototype.findLast` 的 polyfill
* 索引集合类
* `Array`
* `Array.prototype.find()`
* `Array.prototype.findIndex()`
* `Array.prototype.findLastIndex()`
* `Array.prototype.includes()`
* `Array.prototype.filter()`
* `Array.prototype.every()`
* `Array.prototype.some()`
* `TypedArray.prototype.findLast()` (en-US)


Array.prototype.findLastIndex() - JavaScript

Array.prototype.findLastIndex()
===============================

**`findLastIndex()`** 方法反向迭代数组，并返回满足所提供的测试函数的第一个元素的索引。若没有找到对应元素，则返回 -1。

另请参见 `findLast()` 方法，该方法返回最后一个满足测试函数的元素的值（而不是它的索引）。

尝试一下
----

语法
--

```js
findLastIndex(callbackFn)
findLastIndex(callbackFn, thisArg)
```

### 参数

`callbackFn`

对数组中的每个元素执行的函数。回调必须返回一个真值，表示已找到匹配的元素，否则返回一个假值。函数在被调用时会传递以下参数：

`element`

当前遍历到的元素。

`index`

当前正在处理的元素的索引。

`array`

调用 `findLastIndex()` 的数组本身。

`thisArg` 可选

执行 `callbackFn` 时，用作 `this` 的值。参见迭代方法。

### 返回值

数组中通过测试的最后一个（索引最大）元素的索引。如果没有找到任何匹配的元素，则返回 -1。

描述
--

`findLastIndex()` 方法是一个迭代方法。它为数组中的每个元素按索引降序调用一次提供的 `callbackFn` 函数，直到 `callbackFn` 返回一个真值。然后 `findLastIndex()` 返回元素的索引并且停止遍历数组。如果 `callbackFn` 没有返回一个真值，则 `findLastIndex()` 返回 `-1`。

`callbackFn` 会为数组中的*每个*元素调用，而不仅仅是那些被赋值的元素，这意味着对于稀疏数组来说，空槽的行为和 `undefined` 相同。

`findLastIndex()` 方法不会改变调用它的数组，但是提供的 `callbackFn` 可以。但是请注意，数组的长度是在第一次调用 `callbackFn` *之前*保存的。因此：

* `callbackFn` 不会访问在调用 `findLastIndex()` 开始后才添加到数组中的任何元素。
* 对已访问索引的更改不会导致对它们再次调用 `callbackFn` 函数。
* 如果 `callbackFn` 更改了数组中现有的、尚未访问的元素，它传递给`callbackFn` 的值将是该元素被访问时的值。已删除元素被当作 `undefined` 来访问。

**警告：** 上一段描述的并发修改的情况经常导致难以理解的代码，通常应该避免（特殊情况除外）。

`findLastIndex()` 方法是通用的。它只期望 `this` 值具有 `length` 属性和整型键属性。

示例
--

### 查找数组中最后一个素数的索引

下面的示例返回数组中作为素数的最后一个元素的索引，如果没有素数，则返回 `-1`。

```js
function isPrime(element) {
  if (element % 2 === 0 || element < 2) {
    return false;
  }
  for (let factor = 3; factor <= Math.sqrt(element); factor += 2) {
    if (element % factor === 0) {
      return false;
    }
  }
  return true;
}

console.log([4, 6, 8, 12].findLastIndex(isPrime)); // -1，没有找到
console.log([4, 5, 7, 8, 9, 11, 12].findLastIndex(isPrime)); // 5
```

### 在稀疏数组上使用 findLastIndex()

你可以在稀疏数组中搜索 `undefined` 并获得空槽的索引。

```js
console.log([1, , 3].findLastIndex((x) => x === undefined)); // 1
```

### 在非数组对象上调用 findLastIndex()

`findLastIndex()` 方法读取 `this` 的 `length` 属性，然后访问每个整数索引。

```js
const arrayLike = {
  length: 3,
  0: 2,
  1: 7.3,
  2: 4,
};
console.log(
  Array.prototype.findLastIndex.call(arrayLike, (x) => Number.isInteger(x)),
); // 2
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-array.prototype.findlastindex |

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

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

参见
--

* `core-js` 中 `Array.prototype.findIndex` 的 polyfill
* `Array`
* `Array.prototype.find()`
* `Array.prototype.findIndex()`
* `Array.prototype.findLast()`
* `Array.prototype.indexOf()`
* `Array.prototype.lastIndexOf()`
* `TypedArray.prototype.findLastIndex()` (en-US)


Array.prototype.concat() - JavaScript

Array.prototype.concat()
========================

**`concat()`** 方法用于合并两个或多个数组。此方法不会更改现有数组，而是返回一个新数组。

尝试一下
----

语法
--

```js
concat()
concat(value0)
concat(value0, value1)
concat(value0, value1, /\* … ,\*/ valueN)
```

### 参数

`valueN` 可选

数组和/或值，将被合并到一个新的数组中。如果省略了所有 `valueN` 参数，则 `concat` 会返回调用此方法的现存数组的一个浅拷贝。详情请参阅下文描述。

### 返回值

新的 `Array` 实例。

描述
--

`concat` 方法创建一个新数组。该数组将首先由调用它的对象中的元素填充。然后，对于每个参数，它的值将被连接到数组中——对于普通对象或基元，参数本身将成为最终数组的一个元素；对于属性`Symbol.isConcatSpreadable`设置为真的数组或类数组对象，参数的每个元素都将是独立地添加到最终数组中。`concat` 方法不会递归到嵌套数组参数中。

`concat()` 方法是一种复制方法。它不会更改 `this` 或作为参数提供的任何数组，而是返回包含与原始数组中的元素相同的元素的浅拷贝。

如果任何源数组是稀疏数组，`concat()` 方法会保留空槽。

`concat()` 方法是通用的。`this` 值的处理方式与其他参数相同（除了它会先转换为对象），这意味着普通对象将直接添加到结果数组中，而 `@@isConcatSpreadable` 属性为真值的类数组对象将展开并添加到数组中。

示例
--

### 连接两个数组

以下代码将两个数组合并为一个新数组：

```js
const letters = ["a", "b", "c"];
const numbers = [1, 2, 3];

const alphaNumeric = letters.concat(numbers);
console.log(alphaNumeric);
// results in ['a', 'b', 'c', 1, 2, 3]
```

### 连接三个数组

以下代码将三个数组合并为一个新数组：

```js
const num1 = [1, 2, 3];
const num2 = [4, 5, 6];
const num3 = [7, 8, 9];

const numbers = num1.concat(num2, num3);

console.log(numbers);
// results in [1, 2, 3, 4, 5, 6, 7, 8, 9]
```

### 将值连接到数组

以下代码将三个值连接到数组：

```js
const letters = ["a", "b", "c"];

const alphaNumeric = letters.concat(1, [2, 3]);

console.log(alphaNumeric);
// results in ['a', 'b', 'c', 1, 2, 3]
```

### 合并嵌套数组

以下代码合并数组并保留引用：

```js
const num1 = [[1]];
const num2 = [2, [3]];

const numbers = num1.concat(num2);

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

// 修改 num1 的第一个元素
num1[0].push(4);

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

### 使用 Symbol.isConcatSpreadable 合并类数组对象

`concat` 默认情况下不会将类数组对象视作数组——仅在 `Symbol.isConcatSpreadable` 被设置为真值（例如，`true`）时才会将类数组对象视作数组。

```js
const obj1 = { 0: 1, 1: 2, 2: 3, length: 3 };
const obj2 = { 0: 1, 1: 2, 2: 3, length: 3, [Symbol.isConcatSpreadable]: true };
console.log([0].concat(obj1, obj2));
// [ 0, { '0': 1, '1': 2, '2': 3, length: 3 }, 1, 2, 3 ]
```

### 在稀疏数组上使用 concat()

如果任何源数组是稀疏的，则结果数组也将是稀疏的：

```js
console.log([1, , 3].concat([4, 5])); // [1, empty, 3, 4, 5]
console.log([1, 2].concat([3, , 5])); // [1, 2, 3, empty, 5]
```

### 在非数组对象上调用 concat()

如果 `this` 值不是数组，它会被转换为一个对象，然后以与 `concat()` 的参数相同的方式处理。在这种情况下，返回值始终是一个普通的新数组。

```js
console.log(Array.prototype.concat.call({}, 1, 2, 3)); // [{}, 1, 2, 3]
console.log(Array.prototype.concat.call(1, 2, 3)); // [ [Number: 1], 2, 3 ]
const arrayLike = { [Symbol.isConcatSpreadable]: true, length: 2, 0: 1, 1: 2 };
console.log(Array.prototype.concat.call(arrayLike, 3, 4)); // [1, 2, 3, 4]
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-array.prototype.concat |

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

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

参见
--

* 在 `core-js` 中实现 `Array.prototype.concat` 的 Polyfill，修复其中的问题并实现新特性，例如支持 `Symbol.isConcatSpreadable`
* 索引集合类
* `Array`
* `Array.prototype.push()`
* `Array.prototype.unshift()`
* `Array.prototype.splice()`
* `String.prototype.concat()`
* `Symbol.isConcatSpreadable`


Array.prototype.find() - JavaScript

Array.prototype.find()
======================

**`find()`** 方法返回数组中满足提供的测试函数的第一个元素的值。否则返回 `undefined`。

* 如果需要在数组中找到对应元素的**索引**，请使用 `findIndex()`。
* 如果需要查找**某个值的索引**，请使用 `Array.prototype.indexOf()`。（它类似于 `findIndex()`，但只是检查每个元素是否与值相等，而不是使用测试函数。）
* 如果需要查找数组中是否**存在某个值**，请使用 `Array.prototype.includes()`。同样，它检查每个元素是否与值相等，而不是使用测试函数。
* 如果需要查找是否有元素满足所提供的测试函数，请使用 `Array.prototype.some()`。
尝试一下
----

语法
--

```js
find(callbackFn)
find(callbackFn, thisArg)
```

### 参数

`callbackFn`

为数组中的每个元素执行的函数。它应该返回一个真值来表示已经找到了匹配的元素。该函数被调用时将传入以下参数：

`element`

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

`index`

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

`array`

调用了 `find()` 的数组本身。

`thisArg` 可选

执行 `callbackFn` 时用作 `this` 的值。参见迭代方法。

### 返回值

数组中第一个满足所提供测试函数的元素的值，否则返回 `undefined`。

描述
--

`find()` 方法是一个迭代方法。它按索引升序顺序为数组中的每个元素调用提供的 `callbackFn` 函数，直到 `callbackFn` 返回一个真值。然后 `find()` 返回该元素并停止迭代数组。如果 `callbackFn` 从未返回真值，则 `find()` 返回 `undefined`。

`callbackFn` 被调用来处理数组的*每一个*索引，而不仅仅是那些有值的索引。在稀疏数组中，未赋值的空槽与 `undefined` 表现相同。

`find()` 不会改变被调用的数组，但是提供给 `callbackFn` 的函数可能会改变它。但需要注意的是，在第一次调用 `callbackFn` *之前*，数组的长度会被保存。因此：

* 当调用 `find()` 时，`callbackFn` 不会访问超出数组初始长度的任何元素。
* 对已经访问过的索引的更改不会导致再次在这些元素上调用 `callbackFn`。
* 如果 `callbackFn` 改变了数组中已存在但尚未被访问的元素，则传递给 `callbackFn` 的该元素的值将是该元素在被访问时的值。被删除的元素被视为 `undefined`。

**警告：** 上述类型的并发修改经常导致难以理解的代码，通常应避免（特殊情况除外）。

`find()` 方法是通用的。它只期望 `this` 值具有 `length` 属性和整数键属性。

示例
--

### 在对象数组中通过对象属性进行查找

```js
const inventory = [
  { name: "apples", quantity: 2 },
  { name: "bananas", quantity: 0 },
  { name: "cherries", quantity: 5 },
];

function isCherries(fruit) {
  return fruit.name === "cherries";
}

console.log(inventory.find(isCherries));
// { name: 'cherries', quantity: 5 }
```

### 使用箭头函数和解构赋值

```js
const inventory = [
  { name: "apples", quantity: 2 },
  { name: "bananas", quantity: 0 },
  { name: "cherries", quantity: 5 },
];

const result = inventory.find(({ name }) => name === "cherries");

console.log(result); // { name: 'cherries', quantity: 5 }
```

### 寻找数组中的第一个素数

下面的例子展示了如何从数组中寻找素数（如果找不到素数则返回 `undefined`）：

```js
function isPrime(element, index, array) {
  let start = 2;
  while (start <= Math.sqrt(element)) {
    if (element % start++ < 1) {
      return false;
    }
  }
  return element > 1;
}

console.log([4, 6, 8, 12].find(isPrime)); // undefined，未找到
console.log([4, 5, 8, 12].find(isPrime)); // 5
```

### 在稀疏数组上使用 find()

在稀疏数组中，空槽*会被*访问的，并被视为 `undefined`。

```js
// 声明一个在索引 2、3 和 4 处没有元素的数组
const array = [0, 1, , , , 5, 6];

// 将会打印所有索引，而不仅仅是那些有值的非空槽
array.find((value, index) => {
  console.log(`访问索引 ${index}，值为 ${value}`);
});
// 访问索引 0，值为 0
// 访问索引 1，值为 1
// 访问索引 2，值为 undefined
// 访问索引 3，值为 undefined
// 访问索引 4，值为 undefined
// 访问索引 5，值为 5
// 访问索引 6，值为 6

// 打印所有索引，包括已删除的
array.find((value, index) => {
  // 在第一次迭代时删除元素 5
  if (index === 0) {
    console.log(`删除 array[5] 的值 ${array[5]}`);
    delete array[5];
  }
  // 即使删除了，元素 5 仍然被访问
  console.log(`访问索引 ${index}，值为 ${value}`);
});
// 删除值为 5 的 array[5]
// 访问索引 0，值为 0
// 访问索引 1，值为 1
// 访问索引 2，值为 undefined
// 访问索引 3，值为 undefined
// 访问索引 4，值为 undefined
// 访问索引 5，值为 undefined
// 访问索引 6，值为 6
```

### 在非数组对象上调用 find()

`find()` 方法读取 `this` 的 `length` 属性，然后访问每个整数索引。

```js
const arrayLike = {
  length: 3,
  0: 2,
  1: 7.3,
  2: 4,
};
console.log(Array.prototype.find.call(arrayLike, (x) => !Number.isInteger(x)));
// 7.3
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-array.prototype.find |

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

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

参见
--

* `core-js` 中 `Array.prototype.find` 的 polyfill
* 索引集合类
* `Array`
* `Array.prototype.findIndex()`
* `Array.prototype.findLast()`
* `Array.prototype.findLastIndex()`
* `Array.prototype.includes()`
* `Array.prototype.filter()`
* `Array.prototype.every()`
* `Array.prototype.some()`
* `TypedArray.prototype.find()`


Array.prototype.some() - JavaScript

Array.prototype.some()
======================

**`some()`** 方法测试数组中是否至少有一个元素通过了由提供的函数实现的测试。如果在数组中找到一个元素使得提供的函数返回 true，则返回 true；否则返回 false。它不会修改数组。

尝试一下
----

语法
--

```js
some(callbackFn)
some(callbackFn, thisArg)
```

### 参数

`callbackFn`

为数组中的每个元素执行的函数。它应该返回一个真值以指示元素通过测试，否则返回一个假值。该函数被调用时将传入以下参数：

`element`

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

`index`

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

`array`

调用了 `some()` 的数组本身。

`thisArg` 可选

执行 `callbackFn` 时用作 `this` 的值。参见迭代方法。

### 返回值

如果回调函数对数组中至少一个元素返回一个真值，则返回 `true`。否则返回 `false`。

描述
--

`some()` 方法是一个迭代方法。它为数组中的每个元素调用一次指定的 `callbackFn` 函数，直到 `callbackFn` 返回一个真值。如果找到这样的元素，`some()` 方法将会立即返回 `true` 并停止遍历数组。否则，如果 `callbackFn` 对所有元素都返回假值，`some()` 就会返回 `false`。

`some()` 类似于数学中的“存在量词（∃）”。特别地，在对于一个空数组，任何条件下它都返回 `false`。

`callbackFn` 仅针对已分配值的数组索引调用。它不会为稀疏数组中的空槽调用。

`some()` 不会改变调用它的数组，但指定的 `callbackFn` 函数可以。但是请注意，数组的长度是在第一次调用 `callbackFn` *之前*保存的。因此：

* 当开始调用 `some()` 时，`callbackFn` 将不会访问超出数组初始长度的任何元素。
* 对已访问索引的更改不会导致再次在这些元素上调用 `callbackFn`。
* 如果数组中一个现有的、尚未访问的元素被 `callbackFn` 更改，则它传递给 `callbackFn` 的值将是该元素被修改后的值。被删除的元素则不会被访问。

**警告：** 上述类型的并发修改经常导致难以理解的代码，通常应避免（特殊情况除外）。

`some()` 方法是通用的。它只期望 `this` 值具有 `length` 属性和整数键属性。

示例
--

### 测试数组元素的值

下面的例子检测在数组中是否有元素大于 10。

```js
function isBiggerThan10(element, index, array) {
  return element > 10;
}

[2, 5, 8, 1, 4].some(isBiggerThan10); // false
[12, 5, 8, 1, 4].some(isBiggerThan10); // true
```

### 使用箭头函数测试数组元素的值

箭头函数可以通过更简洁的语法实现相同的用例。

```js
[2, 5, 8, 1, 4].some((x) => x > 10); // false
[12, 5, 8, 1, 4].some((x) => x > 10); // true
```

### 判断数组元素中是否存在某个值

此例为模仿 `includes()` 方法，若元素在数组中存在，则回调函数返回值为 `true` :

```js
const fruits = ["apple", "banana", "mango", "guava"];

function checkAvailability(arr, val) {
  return arr.some((arrVal) => val === arrVal);
}

checkAvailability(fruits, "kela"); // false
checkAvailability(fruits, "banana"); // true
```

### 将任意值转换为布尔类型

```js
const TRUTHY\_VALUES = [true, "true", 1];

function getBoolean(value) {
  if (typeof value === "string") {
    value = value.toLowerCase().trim();
  }

  return TRUTHY\_VALUES.some((t) => t === value);
}

getBoolean(false); // false
getBoolean("false"); // false
getBoolean(1); // true
getBoolean("true"); // true
```

### 在稀疏数组上使用 some()

`some()` 不会在空槽上运行它的断言函数。

```js
console.log([1, , 3].some((x) => x === undefined)); // false
console.log([1, , 1].some((x) => x !== 1)); // false
console.log([1, undefined, 1].some((x) => x !== 1)); // true
```

### 在非数组对象上调用 some()

`some()` 方法读取 `this` 的 `length` 属性，然后访问每个整数索引，直到到达末尾或 `callbackFn` 返回 `true`。

```js
const arrayLike = {
  length: 3,
  0: "a",
  1: "b",
  2: "c",
};
console.log(Array.prototype.some.call(arrayLike, (x) => typeof x === "number"));
// false
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-array.prototype.some |

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

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

参见
--

* `core-js` 中 `Array.prototype.every` 的 polyfill
* 索引集合类
* `Array`
* `Array.prototype.every()`
* `Array.prototype.forEach()`
* `Array.prototype.find()`
* `Array.prototype.includes()`
* `TypedArray.prototype.some()`


Array.of() - JavaScript

Array.of()
==========

**`Array.of()`** 静态方法通过可变数量的参数创建一个新的 `Array` 实例，而不考虑参数的数量或类型。

尝试一下
----

语法
--

```js
Array.of()
Array.of(element0)
Array.of(element0, element1)
Array.of(element0, element1, /\* … ,\*/ elementN)
```

### 参数

`elementN`

用于创建数组的元素。

### 返回值

新的 `Array` 实例。

描述
--

`Array.of()` 和 `Array()` 构造函数之间的区别在于对单个参数的处理：`Array.of(7)` 创建一个具有单个元素 `7` 的数组，而 `Array(7)` 创建一个 `length` 为 `7` 的空数组（这意味着一个由 7 个空槽组成的数组，而不是由 7 个 `undefined` 组成的数组）。

```js
Array.of(7); // [7]
Array(7); // 由 7 个空槽组成的数组

Array.of(1, 2, 3); // [1, 2, 3]
Array(1, 2, 3); // [1, 2, 3]
```

`Array.of()` 方法是一个通用的工厂方法。例如，如果 `Array` 的子类继承了 `of()` 方法，继承的 `of()` 方法将返回子类的新实例而不是 `Array` 实例。事实上，`this` 值可以是任何接受单个参数表示新数组长度的构造函数，并且构造函数将与传递给 `of()` 的参数数量一起被调用。当所有元素都被分配时，最终的 `length` 将再次设置。如果 `this` 值不是构造函数，则改用普通的 `Array` 构造函数。

示例
--

### 使用 Array.of()

```js
Array.of(1); // [1]
Array.of(1, 2, 3); // [1, 2, 3]
Array.of(undefined); // [undefined]
```

### 非数组构造函数调用 of()

`of()` 方法可以在任何接受单个参数表示新数组长度的构造函数上调用。

```js
function NotArray(len) {
  console.log("NotArray called with length", len);
}

console.log(Array.of.call(NotArray, 1, 2, 3));
// NotArray called with length 3
// NotArray { '0': 1, '1': 2, '2': 3, length: 3 }

console.log(Array.of.call(Object)); // [Number: 0] { length: 0 }
```

当 `this` 值不是构造函数时，返回一个普通的 `Array` 对象。

```js
console.log(Array.of.call({}, 1)); // [ 1 ]
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-array.of |

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

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

参见
--

* `core-js` 中 `Array.prototype.map` 的 polyfill
* 索引集合类
* `Array`
* `Array()`
* `Array.from()`
* `TypedArray.of()`


Array.fromAsync() - JavaScript

Array.fromAsync()
=================

**`Array.fromAsync()`** 静态方法可以由一个异步可迭代对象、可迭代对象或类数组对象创建一个新的、浅拷贝的 `Array` 实例。

语法
--

```js
Array.fromAsync(arrayLike)
Array.fromAsync(arrayLike, mapFn)
Array.fromAsync(arrayLike, mapFn, thisArg)
```

### 参数

`arrayLike`

要转换为数组的异步可迭代、可迭代或类数组对象。

`mapFn` 可选

为数组中的每个元素执行的函数。如果提供了该函数，则每个要添加到数组中的值都会先通过该函数处理，`mapFn` 的返回值将代替该值被添加到数组中（在等待兑现后）。该函数被调用时将传入以下参数：

`element`

数组中当前正在处理的元素。由于所有元素都会先等待其兑现，因此该值永远不会是 thenable。

`index`

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

`thisArg` 可选

执行 `mapFn` 时用作 `this` 的值。

### 返回值

一个新的 `Promise`，其兑现值是一个新的 `Array` 实例。

描述
--

`Array.fromAsync()` 允许你从以下对象中创建数组：

* 异步可迭代对象（如 `ReadableStream` 和 `AsyncGenerator`）；或者，如果对象不是异步可迭代的，
* 可迭代对象（如 `Map` 和 `Set`）；或者，如果对象是不可迭代的，
* 类数组的对象（带有 `length` 属性和索引元素的对象）。

`Array.fromAsync()` 迭代异步可迭代对象的方式与 `for await...of` 很相似。`Array.fromAsync()` 在行为上与 `Array.from()` 几乎等价，除了以下几点：

* `Array.fromAsync()` 可以处理异步可迭代对象。
* `Array.fromAsync()` 返回一个会兑现为数组实例的 `Promise`。
* 如果使用非异步可迭代对象调用 `Array.fromAsync()`，则要添加到数组中的每个元素（无论是否为 Promise）都会先等待其兑现。
* 如果提供了 `mapFn`，则其输入和输出会在内部等待兑现。

`Array.fromAsync()` 和 `Promise.all()` 都可以将一个 promise 可迭代对象转换为一个数组的 promise。然而，它们有两个关键区别：

* `Array.fromAsync()` 会依次等待对象中产生的每个值兑现。`Promise.all()` 会并行等待所有值兑现。
* `Array.fromAsync()` 惰性迭代可迭代对象，并且不会获取下一个值，直到当前值被兑现。`Promise.all()` 预先获取所有值并等待它们全部兑现。
示例
--

### 从异步可迭代对象创建数组

```js
const asyncIterable = (async function\* () {
  for (let i = 0; i < 5; i++) {
    await new Promise((resolve) => setTimeout(resolve, 10 \* i));
    yield i;
  }
})();

Array.fromAsync(asyncIterable).then((array) => console.log(array));
// [0, 1, 2, 3, 4]
```

### 从同步可迭代对象创建数组

```js
Array.fromAsync(
  new Map([
    [1, 2],
    [3, 4],
  ]),
).then((array) => console.log(array));
// [[1, 2], [3, 4]]
```

### 从产生 promise 的同步可迭代对象创建数组

```js
Array.fromAsync(
  new Set([Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)]),
).then((array) => console.log(array));
// [1, 2, 3]
```

### 从 promise 的类数组对象创建数组

```js
Array.fromAsync({
  length: 3,
  0: Promise.resolve(1),
  1: Promise.resolve(2),
  2: Promise.resolve(3),
}).then((array) => console.log(array));
// [1, 2, 3]
```

### 使用 mapFn

`Array.fromAsync()` 内部会等待 `mapFn` 的输入和输出的兑现。

```js
function delayedValue(v) {
  return new Promise((resolve) => setTimeout(() => resolve(v), 100));
}

Array.fromAsync(
  [delayedValue(1), delayedValue(2), delayedValue(3)],
  (element) => delayedValue(element \* 2),
).then((array) => console.log(array));
// [2, 4, 6]
```

### 与 Promise.all() 的比较

`Array.fromAsync()` 会依次等待对象中产生的每个值兑现。`Promise.all()` 会并行等待所有值兑现。

```js
function\* makeAsyncIterable() {
  for (let i = 0; i < 5; i++) {
    yield new Promise((resolve) => setTimeout(resolve, 100));
  }
}

(async () => {
  console.time("Array.fromAsync() time");
  await Array.fromAsync(makeAsyncIterable());
  console.timeEnd("Array.fromAsync() time");
  // Array.fromAsync() time: 503.610ms

  console.time("Promise.all() time");
  await Promise.all(makeAsyncIterable());
  console.timeEnd("Promise.all() time");
  // Promise.all() time: 101.728ms
})();
```

### 没有对同步可迭代对象的错误处理

如果被迭代的对象是同步可迭代对象，并且在迭代时抛出错误，类似于 `for await...of`，底层迭代器的 `return()` 方法将不会被调用，因此迭代器不会被关闭。

```js
function\* generatorWithRejectedPromises() {
  try {
    yield 0;
    yield Promise.reject(3);
  } finally {
    console.log("called finally");
  }
}

(async () => {
  try {
    await Array.fromAsync(generatorWithRejectedPromises());
  } catch (e) {
    console.log("caught", e);
  }
})();
// caught 3
// 没有“called finally”信息
```

如果需要关闭迭代器，则需要使用 `for...of` 循环，并手动等待每个值兑现。

```js
(async () => {
  const arr = [];
  try {
    for (const val of generatorWithRejectedPromises()) {
      arr.push(await val);
    }
  } catch (e) {
    console.log("caught", e);
  }
})();
// called finally
// caught 3
```

规范
--

| Specification |
| --- |
| ES Array.fromAsync (2022) # sec-array.fromAsync |

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

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

参见
--

* `core-js` 中 `Array.fromAsync` 的 polyfill
* 索引集合类
* `Array`
* `Array()`
* `Array.of()`
* `Array.from()`
* `for await...of`


Array.prototype.with() - JavaScript

Array.prototype.with()
======================

`Array` 实例的 **`with()`** 方法是使用方括号表示法修改指定索引值的复制方法版本。它会返回一个新数组，其指定索引处的值会被新值替换。

语法
--

```js
array.with(index, value)
```

### 参数

`index`

要修改的数组索引（从 0 开始），将会转换为整数。

* 负数索引会从数组末尾开始计数——即当 `index < 0` 时，会使用 `index + array.length`。
* 如果规范化后的索引超出数组边界，会抛出 `RangeError`。

`value`

要分配给指定索引的任何值。

### 返回值

一个全新的数组，其中 `index` 索引处的元素被替换为 `value`。

### 异常

`RangeError`

`index > array.length` 或 `index < -array.length` 时抛出。

描述
--

`with()` 通过返回一个指定索引处的值被新值替换的新数组，来改变数组中指定索引处的值。原数组不会被修改。这使得你可以以链式调用数组方法的方式来对数组进行操作。

`with()` 方法永远不会产生稀疏数组。如果原数组是稀疏的，新数组对应的空白索引位置会替换为 `undefined`。

`with()` 方法是通用的。它只期望 `this` 值具有 `length` 属性和整数键属性。

示例
--

### 创建一个新的数组，改变其中一个元素

```js
const arr = [1, 2, 3, 4, 5];
console.log(arr.with(2, 6)); // [1, 2, 6, 4, 5]
console.log(arr); // [1, 2, 3, 4, 5]
```

### 数组方法的链式调用

使用 `with()` 方法，你可以在更新一个数组元素后继续调用其他的数组方法。

```js
const arr = [1, 2, 3, 4, 5];
console.log(arr.with(2, 6).map((x) => x \*\* 2)); // [1, 4, 36, 16, 25]
```

### 在稀疏数组上使用 with()

`with()` 方法总会创建一个密集数组。

```js
const arr = [1, , 3, 4, , 6];
console.log(arr.with(0, 2)); // [2, undefined, 3, 4, undefined, 6]
```

### 在非数组对象上调用 with()

`with()` 方法会读取 `this` 上的 `length` 属性，之后读取 `this` 上的每个整数键并写入到新数组中，同时 `value` 会被写入指定的 `index`。

```js
const arrayLike = {
  length: 3,
  unrelated: "foo",
  0: 5,
  2: 4,
};
console.log(Array.prototype.with.call(arrayLike, 0, 1));
// [ 1, undefined, 4 ]
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-array.prototype.with |

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

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

参见
--

* `core-js` 中 `Array.prototype.with` 的 polyfill
* `Array.prototype.toReversed()`
* `Array.prototype.toSorted()`
* `Array.prototype.toSpliced()`
* `TypedArray.prototype.with()` (en-US)


Array.prototype.toString() - JavaScript

Array.prototype.toString()
==========================

**`toString()`** 方法返回一个字符串，表示指定的数组及其元素。

尝试一下
----

语法
--

```js
toString()
```

### 返回值

一个表示数组元素的字符串。

描述
--

`Array` 对象覆盖了 `Object` 的 `toString` 方法。数组的 `toString` 方法实际上在内部调用了 `join()` 方法来拼接数组并返回一个包含所有数组元素的字符串，元素之间用逗号分隔。如果 `join` 方法不可用或者不是函数，则会使用 `Object.prototype.toString` 来代替，并返回 `[object Array]`。

```js
const arr = [];
arr.join = 1; // 将 `join` 重新赋值为非函数的值
console.log(arr.toString()); // [object Array]

console.log(Array.prototype.toString.call({ join: () => 1 })); // 1
```

当数组需要被表示为文本值，或者当数组在字符串拼接中被引用时，JavaScript 会自动调用 `toString()` 方法。

示例
--

### 使用 toString()

```js
const array1 = [1, 2, "a", "1a"];

console.log(array1.toString()); // "1,2,a,1a"
```

### 在稀疏数组中使用 toString()

与 `join()` 的行为一致，`toString()` 将空槽视为 `undefined` 并生成一个额外的分隔符：

```js
console.log([1, , 3].toString()); // '1,,3'
```

### 在非数组对象中使用 toString()

`toString()` 是通用的。它期望 `this` 具有 `join()` 方法；如果不存在，则使用 `Object.prototype.toString()`。

```js
console.log(Array.prototype.toString.call({ join: () => 1 }));
// 1; 一个数字
console.log(Array.prototype.toString.call({ join: () => undefined }));
// undefined
console.log(Array.prototype.toString.call({ join: "not function" }));
// "[object Object]"
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-array.prototype.tostring |

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

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

参见
--

* 索引集合类
* `Array`
* `Array.prototype.join()`
* `Array.prototype.toLocaleString()`
* `TypedArray.prototype.toString()`
* `String.prototype.toString()`


Array.prototype.reduce() - JavaScript

Array.prototype.reduce()
========================

**`reduce()`** 方法对数组中的每个元素按序执行一个提供的 **reducer** 函数，每一次运行 **reducer** 会将先前元素的计算结果作为参数传入，最后将其结果汇总为单个返回值。

第一次执行回调函数时，不存在“上一次的计算结果”。如果需要回调函数从数组索引为 0 的元素开始执行，则需要传递初始值。否则，数组索引为 0 的元素将被用作初始值，迭代器将从第二个元素开始执行（即从索引为 1 而不是 0 的位置开始）。

下面的例子能够帮助你理解 `reduce()` 的用处——计算数组所有元素的总和：

尝试一下
----

reducer 逐个遍历数组元素，每一步都将当前元素的值与前一步的结果相加（该结果是之前所有步骤结果的总和）——直到没有更多需要相加的元素。

语法
--

```js
reduce(callbackFn)
reduce(callbackFn, initialValue)
```

### 参数

`callbackFn`

为数组中每个元素执行的函数。其返回值将作为下一次调用 `callbackFn` 时的 `accumulator` 参数。对于最后一次调用，返回值将作为 `reduce()` 的返回值。该函数被调用时将传入以下参数：

`accumulator`

上一次调用 `callbackFn` 的结果。在第一次调用时，如果指定了 `initialValue` 则为指定的值，否则为 `array[0]` 的值。

`currentValue`

当前元素的值。在第一次调用时，如果指定了 `initialValue`，则为 `array[0]` 的值，否则为 `array[1]`。

`currentIndex`

`currentValue` 在数组中的索引位置。在第一次调用时，如果指定了 `initialValue` 则为 `0`，否则为 `1`。

`array`

调用了 `reduce()` 的数组本身。

`initialValue` 可选

第一次调用回调时初始化 `accumulator` 的值。如果指定了 `initialValue`，则 `callbackFn` 从数组中的第一个值作为 `currentValue` 开始执行。如果没有指定 `initialValue`，则 `accumulator` 初始化为数组中的第一个值，并且 `callbackFn` 从数组中的第二个值作为 `currentValue` 开始执行。在这种情况下，如果数组为空（没有第一个值可以作为 `accumulator` 返回），则会抛出错误。

### 返回值

使用“reducer”回调函数遍历整个数组后的结果。

### 异常

`TypeError`

如果数组为空且未提供 `initialValue`，则会抛出异常。

描述
--

`reduce()` 方法是一个迭代方法。它按升序对数组中的所有元素运行一个“reducer”回调函数，并将它们累积到一个单一的值中。每次调用时，`callbackFn` 的返回值都作为 `accumulator` 参数传递到下一次调用中。`accumulator` 的最终值（也就是在数组的最后一次迭代中从 `callbackFn` 返回的值）将作为 `reduce()` 的返回值。

`callbackFn` 仅对已分配值的数组索引进行调用。不会对稀疏数组中的空槽进行调用。

与其他迭代方法不同，`reduce()` 不接受 `thisArg` 参数。`callbackFn` 调用时始终以 `undefined` 作为 `this` 的值，如果 `callbackFn` 未处于严格模式，则该值将被替换为 `globalThis`。

`reduce()` 是函数式编程中的一个核心概念，在函数式编程中，不可能改变任何值，因此为了累积数组中的所有值，必须在每次迭代中返回一个新的累加器。这种约定也适用于 JavaScript 的 `reduce()`：应该在可能的情况下使用展开语法或其他复制方法来创建新的数组和对象作为累加器，而不是改变现有的累加器。如果你决定改变累加器而不是复制它，请记得仍然在回调中返回修改后的对象，否则下一次迭代将收到 `undefined`。

`reduce()` 不会改变被调用的数组，但是作为 `callbackFn` 提供的函数可能会改变数组。但需要注意的是，在第一次调用 `callbackFn` *之前*，数组的长度会被保存。因此：

* 当开始调用 `reduce()` 时，`callbackFn` 将不会访问超出数组初始长度的任何元素。
* 对已访问索引的更改不会导致再次在这些元素上调用 `callbackFn`。
* 如果数组中一个现有的、尚未访问的元素被 `callbackFn` 更改，则它传递给 `callbackFn` 的值将是该元素被修改后的值。被删除的元素则不会被访问。

**警告：** 上述类型的并发修改经常导致难以理解的代码，通常应避免（特殊情况除外）。

`reduce()` 方法是通用的。它只期望 `this` 值具有 `length` 属性和整数键属性。

### 不应使用 reduce() 的情况

像 `reduce()` 这样的递归函数可能非常强大，但有时可能很难理解，特别是对于缺乏经验的 JavaScript 开发人员。如果使用其他数组方法可以使代码更清晰，则开发人员必须权衡代码可读性与使用 `reduce()` 带来的好处。如果 `reduce()` 确实是最佳选择，应该通过良好的文档和语义化的变量命名来提高代码的可读性。

### 边界情况

如果数组只有一个元素（无论位置如何）且未提供 `initialValue`，或者提供了 `initialValue` 但数组为空，则将返回该单个值，而*不*调用 `callbackFn`。

如果提供了 `initialValue` 且数组不为空，则 `reduce` 方法将始终从索引 0 开始调用回调函数。

如果未提供 `initialValue`，则对于长度大于 1、等于 1 和 0 的数组，`reduce` 方法将有不同的表现，如以下示例所示：

```js
const getMax = (a, b) => Math.max(a, b);

// 从索引 0 开始为数组中的每个元素调用回调函数
[1, 100].reduce(getMax, 50); // 100
[50].reduce(getMax, 10); // 50

// 仅为索引 1 处的元素调用回调函数
[1, 100].reduce(getMax); // 100

// 不调用回调函数
[50].reduce(getMax); // 50
[].reduce(getMax, 1); // 1

[].reduce(getMax); // TypeError
```

示例
--

### 无初始值时 reduce() 如何运行

假如运行以下无初始值的 `reduce()` 代码：

```js
const array = [15, 16, 17, 18, 19];

function reducer(accumulator, currentValue, index) {
  const returns = accumulator + currentValue;
  console.log(
    `accumulator: ${accumulator}, currentValue: ${currentValue}, index: ${index}, returns: ${returns}`,
  );
  return returns;
}

array.reduce(reducer);
```

回调函数会被调用四次，每次调用的参数和返回值如下表：

|  | `accumulator` | `currentValue` | `index` | 返回值 |
| --- | --- | --- | --- | --- |
| 第一次调用 | `15` | `16` | `1` | `31` |
| 第二次调用 | `31` | `17` | `2` | `48` |
| 第三次调用 | `48` | `18` | `3` | `66` |
| 第四次调用 | `66` | `19` | `4` | `85` |

`array` 参数在整个过程中始终不会改变——它始终是 `[15, 16, 17, 18, 19]`。`reduce()` 返回的值将是最后一次回调返回值（`85`）。

### 有初始值时 reduce() 如何运行

在这里，我们以相同的算法 reduce 同一个数组，但提供 `10` 作为 `initialValue`：

```js
[15, 16, 17, 18, 19].reduce(
  (accumulator, currentValue) => accumulator + currentValue,
  10,
);
```

回调函数会被调用五次，每次调用的参数和返回值如下表：

|  | `accumulator` | `currentValue` | `index` | 返回值 |
| --- | --- | --- | --- | --- |
| 第一次调用 | `10` | `15` | `0` | `25` |
| 第二次调用 | `25` | `16` | `1` | `41` |
| 第三次调用 | `41` | `17` | `2` | `58` |
| 第四次调用 | `58` | `18` | `3` | `76` |
| 第五次调用 | `76` | `19` | `4` | `95` |

这种情况下 `reduce()` 返回的值是 `95`。

### 求对象数组中值的总和

为了对包含在对象数组中的值进行求和，**必须**提供一个 `initialValue`，以便每个项都通过回调函数处理。

```js
const objects = [{ x: 1 }, { x: 2 }, { x: 3 }];
const sum = objects.reduce(
  (accumulator, currentValue) => accumulator + currentValue.x,
  0,
);

console.log(sum); // 6
```

### 展平嵌套数组

```js
const flattened = [
  [0, 1],
  [2, 3],
  [4, 5],
].reduce((accumulator, currentValue) => accumulator.concat(currentValue), []);
// flattened 的值是 [0, 1, 2, 3, 4, 5]
```

### 统计对象中值的出现次数

```js
const names = ["Alice", "Bob", "Tiff", "Bruce", "Alice"];

const countedNames = names.reduce((allNames, name) => {
  const currCount = allNames[name] ?? 0;
  return {
    ...allNames,
    [name]: currCount + 1,
  };
}, {});
// countedNames 的值是：
// { 'Alice': 2, 'Bob': 1, 'Tiff': 1, 'Bruce': 1 }
```

### 按属性对对象进行分组

```js
const people = [
  { name: "Alice", age: 21 },
  { name: "Max", age: 20 },
  { name: "Jane", age: 20 },
];

function groupBy(objectArray, property) {
  return objectArray.reduce((acc, obj) => {
    const key = obj[property];
    const curGroup = acc[key] ?? [];

    return { ...acc, [key]: [...curGroup, obj] };
  }, {});
}

const groupedPeople = groupBy(people, "age");
console.log(groupedPeople);
// {
// 20: [
// { name: 'Max', age: 20 },
// { name: 'Jane', age: 20 }
// ],
// 21: [{ name: 'Alice', age: 21 }]
// }
```

### 使用展开语法和 initialValue 连接包含在对象数组中的数组

```js
// friends——一个对象数组，其中对象字段“books”是最喜欢的书的列表
const friends = [
  {
    name: "Anna",
    books: ["Bible", "Harry Potter"],
    age: 21,
  },
  {
    name: "Bob",
    books: ["War and peace", "Romeo and Juliet"],
    age: 26,
  },
  {
    name: "Alice",
    books: ["The Lord of the Rings", "The Shining"],
    age: 18,
  },
];

// allbooks——列表，其中包含所有朋友的书籍和 initialValue 中包含的附加列表
const allbooks = friends.reduce(
  (accumulator, currentValue) => [...accumulator, ...currentValue.books],
  ["Alphabet"],
);
console.log(allbooks);
// [
// 'Alphabet', 'Bible', 'Harry Potter', 'War and peace',
// 'Romeo and Juliet', 'The Lord of the Rings',
// 'The Shining'
// ]
```

### 数组去重

**备注：** 可以使用 `Set` 和 `Array.from()` 来实现相同的效果，如 `const arrayWithNoDuplicates = Array.from(new Set(myArray))`，并且性能更好。

```js
const myArray = ["a", "b", "a", "b", "c", "e", "e", "c", "d", "d", "d", "d"];
const myArrayWithNoDuplicates = myArray.reduce((accumulator, currentValue) => {
  if (!accumulator.includes(currentValue)) {
    return [...accumulator, currentValue];
  }
  return accumulator;
}, []);

console.log(myArrayWithNoDuplicates);
```

### 使用 reduce() 来替代 .filter().map()

使用 `filter()` 和 `map()` 会遍历数组两次，但是你可以使用 `reduce()` 只遍历一次并实现相同的效果，从而更高效。（如果你喜欢使用 `for` 循环，你可以在遍历一次时使用 `forEach()` 进行过滤和映射。）

```js
const numbers = [-5, 6, 2, 0];

const doubledPositiveNumbers = numbers.reduce((accumulator, currentValue) => {
  if (currentValue > 0) {
    const doubled = currentValue \* 2;
    return [...accumulator, doubled];
  }
  return accumulator;
}, []);

console.log(doubledPositiveNumbers); // [12, 4]
```

### 按顺序运行 Promise

```js
/\*\*
 \* 链接一系列 Promise 处理程序。
 \*
 \* @param {array} arr——一个 Promise 处理程序列表，每个处理程序接收前一个处理程序解决的结果并返回另一个 Promise。
 \* @param {\*} input——开始调用 Promise 链的初始值
 \* @return {Object}——由一系列 Promise 链接而成的 Promise
 \*/
function runPromiseInSequence(arr, input) {
  return arr.reduce(
    (promiseChain, currentFunction) => promiseChain.then(currentFunction),
    Promise.resolve(input),
  );
}

// Promise 函数 1
function p1(a) {
  return new Promise((resolve, reject) => {
    resolve(a \* 5);
  });
}

// Promise 函数 2
function p2(a) {
  return new Promise((resolve, reject) => {
    resolve(a \* 2);
  });
}

// 函数 3——将由 `.then()` 包装在已解决的 Promise 中
function f3(a) {
  return a \* 3;
}

// Promise 函数 4
function p4(a) {
  return new Promise((resolve, reject) => {
    resolve(a \* 4);
  });
}

const promiseArr = [p1, p2, f3, p4];
runPromiseInSequence(promiseArr, 10).then(console.log); // 1200
```

### 使用函数组合实现管道

```js
// 组合使用的构建块
const double = (x) => 2 \* x;
const triple = (x) => 3 \* x;
const quadruple = (x) => 4 \* x;

// 函数组合，实现管道功能
const pipe =
  (...functions) =>
  (initialValue) =>
    functions.reduce((acc, fn) => fn(acc), initialValue);

// 组合的函数，实现特定值的乘法
const multiply6 = pipe(double, triple);
const multiply9 = pipe(triple, triple);
const multiply16 = pipe(quadruple, quadruple);
const multiply24 = pipe(double, triple, quadruple);

// 用例
multiply6(6); // 36
multiply9(9); // 81
multiply16(16); // 256
multiply24(10); // 240
```

### 在稀疏数组中使用 reduce()

`reduce()` 会跳过稀疏数组中缺失的元素，但不会跳过 `undefined` 值。

```js
console.log([1, 2, , 4].reduce((a, b) => a + b)); // 7
console.log([1, 2, undefined, 4].reduce((a, b) => a + b)); // NaN
```

### 在非数组对象上调用 reduce()

`reduce()` 方法读取 `this` 的 `length` 属性，然后访问每个整数索引。

```js
const arrayLike = {
  length: 3,
  0: 2,
  1: 3,
  2: 4,
};
console.log(Array.prototype.reduce.call(arrayLike, (x, y) => x + y));
// 9
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-array.prototype.reduce |

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

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

参见
--

* `core-js` 中 `Array.prototype.reduce` 的 polyfill
* 索引集合类
* `Array`
* `Array.prototype.map()`
* `Array.prototype.flat()`
* `Array.prototype.flatMap()`
* `Array.prototype.reduceRight()`
* `TypedArray.prototype.reduce()`
* `Object.groupBy()`
* `Map.groupBy()`


Array.prototype.entries() - JavaScript

Array.prototype.entries()
=========================

**`entries()`** 方法返回一个新的*数组迭代器*对象，该对象包含数组中每个索引的键/值对。

尝试一下
----

语法
--

```js
entries()
```

### 返回值

一个新的可迭代迭代器对象。

描述
--

当在稀疏数组上使用时，`entries()` 方法迭代空槽，就像它们的值为 `undefined` 一样。

`entries()` 方法是通用的。它只要求 `this` 值具有 `length` 属性和以整数为键的属性。

示例
--

### 迭代索引和元素

```js
const a = ["a", "b", "c"];

for (const [index, element] of a.entries()) {
  console.log(index, element);
}

// 0 'a'
// 1 'b'
// 2 'c'
```

### 使用 for...of 循环

```js
const array = ["a", "b", "c"];
const arrayEntries = array.entries();

for (const element of arrayEntries) {
  console.log(element);
}

// [0, 'a']
// [1, 'b']
// [2, 'c']
```

### 迭代稀疏数组

`entries()` 将访问空槽，就像它们是 `undefined` 一样。

```js
for (const element of [, "a"].entries()) {
  console.log(element);
}
// [0, undefined]
// [1, 'a']
```

### 在非数组对象上调用 entries()

`entries()` 方法读取 `this` 的 `length` 属性，然后访问每个整数索引。

```js
const arrayLike = {
  length: 3,
  0: "a",
  1: "b",
  2: "c",
};
for (const entry of Array.prototype.entries.call(arrayLike)) {
  console.log(entry);
}
// [ 0, 'a' ]
// [ 1, 'b' ]
// [ 2, 'c' ]
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-array.prototype.entries |

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

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

参见
--

* `core-js` 中 `Array.prototype.entries` 的 polyfill
* 索引集合类
* `Array`
* `Array.prototype.keys()`
* `Array.prototype.values()`
* `Array.prototype[@@iterator]()`
* `TypedArray.prototype.entries()`
* 迭代协议


Array.isArray() - JavaScript

Array.isArray()
===============

**`Array.isArray()`** 静态方法用于确定传递的值是否是一个数组。

尝试一下
----

语法
--

```js
Array.isArray(value)
```

### 参数

`value`

需要检测的值。

### 返回值

如果 `value` 是 `Array`，则为 `true`；否则为 `false`。如果 `value` 是 `TypedArray` 实例，则总是返回 `false`。

描述
--

`Array.isArray()` 检查传递的值是否为 `Array`。它不检查值的原型链，也不依赖于它所附加的 `Array` 构造函数。对于使用数组字面量语法或 `Array` 构造函数创建的任何值，它都会返回 `true`。这使得它可以安全地使用跨领域（cross-realm）对象，其中 `Array` 构造函数的标识是不同的，因此会导致 `instanceof Array` 失败。

有关更多细节，请参阅文章“确定 JavaScript 对象是否为数组”。

`Array.isArray()` 也拒绝原型链中带有 `Array.prototype`，而实际不是数组的对象，但 `instanceof Array` 会接受。

示例
--

### 使用 Array.isArray()

```js
// 下面的函数调用都返回 true
Array.isArray([]);
Array.isArray([1]);
Array.isArray(new Array());
Array.isArray(new Array("a", "b", "c", "d"));
Array.isArray(new Array(3));
// 鲜为人知的事实：其实 Array.prototype 也是一个数组：
Array.isArray(Array.prototype);

// 下面的函数调用都返回 false
Array.isArray();
Array.isArray({});
Array.isArray(null);
Array.isArray(undefined);
Array.isArray(17);
Array.isArray("Array");
Array.isArray(true);
Array.isArray(false);
Array.isArray(new Uint8Array(32));
// 这不是一个数组，因为它不是使用数组字面量语法或 Array 构造函数创建的
Array.isArray({ \_\_proto\_\_: Array.prototype });
```

### instanceof 和 Array.isArray()

当检测 `Array` 实例时，`Array.isArray` 优于 `instanceof`，因为 `Array.isArray` 能跨领域工作。

```js
const iframe = document.createElement("iframe");
document.body.appendChild(iframe);
const xArray = window.frames[window.frames.length - 1].Array;
const arr = new xArray(1, 2, 3); // [1, 2, 3]

// 正确检查 Array
Array.isArray(arr); // true
// arr 的原型是 xArray.prototype，它是一个不同于 Array.prototype 的对象
arr instanceof Array; // false
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-array.isarray |

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

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

参见
--

* `core-js` 中 `Array.isArray` 的 polyfill
* 索引集合类指南
* `Array`


Array.prototype.toSorted() - JavaScript

Array.prototype.toSorted()
==========================

`Array` 实例的 **`toSorted()`** 方法是 `sort()` 方法的复制方法版本。它返回一个新数组，其元素按升序排列。

语法
--

```js
// 不传入函数
toSorted()

// 传入箭头函数
toSorted((a, b) => { /\* … \*/ })

// 传入比较函数
toSorted(compareFn)

// 內联比较函数
toSorted(function compareFn(a, b) { /\* … \*/ })
```

### 参数

`compareFn` 可选

指定一个定义排序顺序的函数。如果省略，则将数组元素转换为字符串，然后根据每个字符的 Unicode 码位值进行排序。

`a`

用于比较的第一个元素。

`b`

用于比较的第二个元素。

### 返回值

一个新数组，其元素按升序排序。

描述
--

有关 `compareFn` 参数的更多信息，请参阅 `sort()`。

当在稀疏数组上使用 `toSorted()` 方法时，它迭代时会将空槽视为具有 `undefined` 值的元素。

`toSorted()` 方法是通用的，它只期望 `this` 值具有 `length` 属性和整数键属性。

示例
--

### 对数组进行排序

```js
const months = ["Mar", "Jan", "Feb", "Dec"];
const sortedMonths = months.toSorted();
console.log(sortedMonths); // ['Dec', 'Feb', 'Jan', 'Mar']
console.log(months); // ['Mar', 'Jan', 'Feb', 'Dec']

const values = [1, 10, 21, 2];
const sortedValues = values.toSorted((a, b) => a - b);
console.log(sortedValues); // [1, 2, 10, 21]
console.log(values); // [1, 10, 21, 2]
```

有关更多用法示例，请参见 `sort()`。

### 在稀疏数组上使用 toSorted()

空槽被视为具有 `undefined` 值而被排序。它们总是排序到数组的末尾，并且 `compareFn` 不会对它们进行调用。

```js
console.log(["a", "c", , "b"].toSorted()); // ['a', 'b', 'c', undefined]
console.log([, undefined, "a", "b"].toSorted()); // ["a", "b", undefined, undefined]
```

### 在非数组对象上调用 toSorted()

`toSorted()` 方法会读取 `this` 的 `length` 属性。然后它会收集所有在 `0` 到 `length - 1` 范围内的整数键属性，对它们进行排序并将它们写入一个新的数组中。

```js
const arrayLike = {
  length: 3,
  unrelated: "foo",
  0: 5,
  2: 4,
};
console.log(Array.prototype.toSorted.call(arrayLike));
// [4, 5, undefined]
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-array.prototype.tosorted |

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

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

参见
--

* `core-js` 中 `Array.prototype.with` 的 polyfill
* `Array.prototype.sort()`
* `Array.prototype.toReversed()`
* `Array.prototype.toSpliced()`
* `Array.prototype.with()`
* `TypedArray.prototype.toSorted()` (en-US)


Array.prototype.flatMap() - JavaScript

Array.prototype.flatMap()
=========================

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

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

* Learn more
* See full compatibility
* Report feedback
**`flatMap()`** 方法对数组中的每个元素应用给定的回调函数，然后将结果展开一级，返回一个新数组。它等价于在调用 `map()` 方法后再调用深度为 1 的 `flat()` 方法（`arr.map(...args).flat()`），但比分别调用这两个方法稍微更高效一些。

尝试一下
----

语法
--

```js
flatMap(callbackFn)
flatMap(callbackFn, thisArg)
```

### 参数

`callbackFn`

一个在数组的每个元素上执行的函数。它应该返回一个包含新数组元素的数组，或是要添加到新数组中的单个非数组值。该函数将被传入以下参数：

`element`

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

`index`

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

`array`

调用 `flatMap()` 的当前数组。

`thisArg` 可选

在执行 `callbackFn` 时用作 `this` 的值。参见迭代方法。

### 返回值

一个新的数组，其中每个元素都是回调函数的结果，并且被展开一级。

描述
--

`flatMap()` 方法是一个迭代方法。有关回调函数的详细说明，请参见 `Array.prototype.map()`。`flatMap()` 方法等同于调用 `map(callbackFn, thisArg)` 后再调用 `flat(1)`——对于每个元素，它都会生成一个新元素数组，并将生成的数组连接起来形成一个新数组。

`flatMap()` 方法是通用的。它只需要 `this` 值具有 `length` 属性和整数键属性即可。但是，如果要展开从回调函数 `callbackFn` 返回的值，则该值必须是数组。

### 替代方案

#### 预分配数组并显式迭代

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

arr.flatMap((x) => [x, x \* 2]);
// 等价于
const n = arr.length;
const acc = new Array(n \* 2);
for (let i = 0; i < n; i++) {
  const x = arr[i];
  acc[i \* 2] = x;
  acc[i \* 2 + 1] = x \* 2;
}
// [1, 2, 2, 4, 3, 6, 4, 8]
```

需要注意的是，在这种特定情况下，`flatMap` 方法的性能比 for 循环要慢——因为需要创建临时数组并进行垃圾回收，同时返回的数组不需要经常调整大小。但是，在注重灵活性和可读性的情况下，`flatMap` 仍可能是正确的解决方案。

示例
--

### map() 与 flatMap()

```js
const arr1 = [1, 2, 3, 4];

arr1.map((x) => [x \* 2]);
// [[2], [4], [6], [8]]

arr1.flatMap((x) => [x \* 2]);
// [2, 4, 6, 8]

// 只有一层被展平
arr1.flatMap((x) => [[x \* 2]]);
// [[2], [4], [6], [8]]
```

虽然上面的示例可以通过使用 `map()` 方法自行实现，但下面的示例更好地展示了 `flatMap()` 方法的用法。

让我们从一组句子中生成单词列表。

```js
const arr1 = ["it's Sunny in", "", "California"];

arr1.map((x) => x.split(" "));
// [["it's","Sunny","in"],[""],["California"]]

arr1.flatMap((x) => x.split(" "));
// ["it's","Sunny","in", "", "California"]
```

请注意，输出列表长度可能与输入列表长度不同。

### 在 `map()` 方法过程中添加和删除元素

`flatMap` 方法可以用作在 `map` 方法中添加和删除元素（修改元素数量）的方法。换句话说，它允许你将*一些项处理为另一些项*（通过单独处理每个输入项），而不总是*一对一*的。在这个意义上，它的作用类似于 filter 的相反操作。如果要保留该项，则返回一个包含该项的单元素数组，如果要添加元素，则返回一个包含多个元素的数组，如果要删除该项，则返回一个空数组。

```js
// 假设我们想要删除所有负数，并将奇数拆分成偶数和 1
const a = [5, 4, -3, 20, 17, -33, -4, 18];
// |\ \ x | | \ x x |
// [4,1, 4, 20, 16, 1, 18]

const result = a.flatMap((n) => {
  if (n < 0) {
    return [];
  }
  return n % 2 === 0 ? [n] : [n - 1, 1];
});
console.log(result); // [4, 1, 4, 20, 16, 1, 18]
```

### 在稀疏数组上使用 flatMap()

`callbackFn` 不会被源数组中的空槽调用，因为 `map()` 不会调用，而 `flat()` 将忽略返回数组中的空槽。

```js
console.log([1, 2, , 4, 5].flatMap((x) => [x, x \* 2])); // [1, 2, 2, 4, 4, 8, 5, 10]
console.log([1, 2, 3, 4].flatMap((x) => [, x \* 2])); // [2, 4, 6, 8]
```

### 在非数组对象上调用 flatMap()

`flatMap()` 方法读取 `this` 的 `length` 属性，然后访问每个整数索引。如果回调函数的返回值不是数组，则始终直接将其附加到结果数组的末尾。

```js
const arrayLike = {
  length: 3,
  0: 1,
  1: 2,
  2: 3,
};
console.log(Array.prototype.flatMap.call(arrayLike, (x) => [x, x \* 2]));
// [1, 2, 2, 4, 3, 6]

// 回调函数返回的类数组对象不会被展平
console.log(
  Array.prototype.flatMap.call(arrayLike, (x) => ({
    length: 1,
    0: x,
  })),
);
// [ { '0': 1, length: 1 }, { '0': 2, length: 1 }, { '0': 3, length: 1 } ]
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-array.prototype.flatmap |

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

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

参见
--

* `core-js` 中 `Array.prototype.flatMap` 的 polyfill
* 索引集合类
* `Array`
* `Array.prototype.concat()`
* `Array.prototype.flat()`
* `Array.prototype.map()`
* `Array.prototype.reduce()`


Array.prototype.join() - JavaScript

Array.prototype.join()
======================

**`join()`** 方法将一个数组（或一个类数组对象）的所有元素连接成一个字符串并返回这个字符串，用逗号或指定的分隔符字符串分隔。如果数组只有一个元素，那么将返回该元素而不使用分隔符。

尝试一下
----

语法
--

```js
join()
join(separator)
```

### 参数

`separator` 可选

指定一个字符串来分隔数组的每个元素。如果需要，将分隔符转换为字符串。如果省略，数组元素用逗号（`,`）分隔。如果 `separator` 是空字符串（`""`），则所有元素之间都没有任何字符。

### 返回值

一个所有数组元素连接的字符串。如果 `arr.length` 为 `0`，则返回空字符串。

描述
--

所有数组元素被转换成字符串并连接到一个字符串中。如果一个元素是 `undefined` 或 `null`，它将被转换为空字符串，而不是字符串 `"undefined"` 或 `"null"`。

`Array.prototype.toString()` 会在内部访问 `join` 方法，不带参数。覆盖一个数组实例的 `join` 也将覆盖它的 `toString` 行为。

当在稀疏数组上使用时，`join()` 方法迭代空槽，就像它们的值为 `undefined` 一样。

`join()` 方法是通用的。它只期望 `this` 值具有 `length` 属性和整数键属性。

示例
--

### 使用用四种不同的方式连接数组

下面的示例创建一个数组 `a`，其中包含三个元素，然后用四种不同的分隔符连接所有数组元素。首先是默认的分隔符逗号，然后是一个逗号加空格，接下来是一个加号前后加空格，最后是一个空字符串。

```js
const a = ["Wind", "Water", "Fire"];
a.join(); // 'Wind,Water,Fire'
a.join(", "); // 'Wind, Water, Fire'
a.join(" + "); // 'Wind + Water + Fire'
a.join(""); // 'WindWaterFire'
```

### 在稀疏数组上使用 join()

`join()` 将空槽视为 `undefined`，并产生额外的分隔符：

```js
console.log([1, , 3].join()); // '1,,3'
console.log([1, undefined, 3].join()); // '1,,3'
```

### 在非数组对象上调用 join()

`join()` 方法读取 `this` 的 `length` 属性，然后访问每个整数索引。

```js
const arrayLike = {
  length: 3,
  0: 2,
  1: 3,
  2: 4,
};
console.log(Array.prototype.join.call(arrayLike));
// 2,3,4
console.log(Array.prototype.join.call(arrayLike, "."));
// 2.3.4
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-array.prototype.join |

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

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

参见
--

* `core-js` 中 `Array.prototype.join` 的 polyfill
* 索引集合类
* `Array`
* `Array.prototype.toString()`
* `TypedArray.prototype.join()`
* `String.prototype.split()`


Array.prototype.fill() - JavaScript

Array.prototype.fill()
======================

**`fill()`** 方法用一个固定值填充一个数组中从起始索引（默认为 `0`）到终止索引（默认为 `array.length`）内的全部元素。它返回修改后的数组。

尝试一下
----

语法
--

```js
fill(value)
fill(value, start)
fill(value, start, end)
```

### 参数

`value`

用来填充数组元素的值。注意所有数组中的元素都将是这个确定的值：如果 `value` 是个对象，那么数组的每一项都会引用这个元素。

`start` 可选

基于零的索引，从此开始填充，转换为整数。

* 负数索引从数组的末端开始计算，如果 `start < 0`，则使用 `start + array.length`。
* 如果 `start < -array.length` 或 `start` 被省略，则使用 `0`。
* 如果 `start >= array.length`，没有索引被填充。

`end` 可选

基于零的索引，在此结束填充，转换为整数。`fill()` 填充到但不包含 `end` 索引。

* 负数索引从数组的末端开始计算，如果 `end < 0`，则使用 `end + array.length`。
* 如果 `end < -array.length`，则使用 `0`。
* 如果 `end >= array.length` 或 `end` 被省略，则使用 `array.length`，导致所有索引都被填充。
* 如果经标准化后，`end` 的位置在 `start` 之前或之上，没有索引被填充。

### 返回值

经 `value` 填充修改后的数组。

描述
--

`fill()` 是个修改方法，不会改变 `this` 的长度，但会改变 `this` 的内容。

`fill()` 也会使用 `value` 填充稀疏数组的空槽。

`fill()` 方法是通用的。它只期望 `this` 值有一个 `length` 属性。虽然字符串也是类似数组的，但这个方法不适合应用于它们，因为字符串是不可变的。

**备注：** 在一个空数组（`length = 0`）上使用 `Array.prototype.fill()` 不会对其进行修改，因为数组没有要修改的内容。要在声明一个数组时使用 `Array.prototype.fill()`，请确保数组的 `length` 不为零。参见示例。

示例
--

### 使用 fill

```js
console.log([1, 2, 3].fill(4)); // [4, 4, 4]
console.log([1, 2, 3].fill(4, 1)); // [1, 4, 4]
console.log([1, 2, 3].fill(4, 1, 2)); // [1, 4, 3]
console.log([1, 2, 3].fill(4, 1, 1)); // [1, 2, 3]
console.log([1, 2, 3].fill(4, 3, 3)); // [1, 2, 3]
console.log([1, 2, 3].fill(4, -3, -2)); // [4, 2, 3]
console.log([1, 2, 3].fill(4, NaN, NaN)); // [1, 2, 3]
console.log([1, 2, 3].fill(4, 3, 5)); // [1, 2, 3]
console.log(Array(3).fill(4)); // [4, 4, 4]

// 一个简单的对象，被数组的每个空槽所引用
const arr = Array(3).fill({}); // [{}, {}, {}]
arr[0].hi = "hi"; // [{ hi: "hi" }, { hi: "hi" }, { hi: "hi" }]
```

### 使用 fill() 创建全 1 矩阵

此示例演示了如何创建全 1 的矩阵，就像 Octave 或 MATLAB 的 `ones()` 函数做到的那样。

```js
const arr = new Array(3);
for (let i = 0; i < arr.length; i++) {
  arr[i] = new Array(4).fill(1); // 创建一个大小为 4 的数组，填充全 1
}
arr[0][0] = 10;
console.log(arr[0][0]); // 10
console.log(arr[1][0]); // 1
console.log(arr[2][0]); // 1
```

### 使用 fill() 填充空数组

此示例演示了如何填充数组，将所有值设定为一个特定的值。无需指定 `end` 参数。

```js
const tempGirls = Array(5).fill("girl", 0);
```

注意，数组最初为稀疏数组，没有分配索引。`fill()` 仍然可以填充这个数组。

### 在非数组对象上调用 fill()

`fill()` 方法读取 `this` 的 `length` 属性，并设置从 `start` 到 `end` 的每个整数属性的值。

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

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-array.prototype.fill |

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

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

参见
--

* `core-js` 中 `Array.prototype.fill` 的 polyfill
* 索引集合类
* `Array`
* `TypedArray.prototype.fill()`


Array.prototype.includes() - JavaScript

Array.prototype.includes()
==========================

**`includes()`** 方法用来判断一个数组是否包含一个指定的值，根据情况，如果包含则返回 `true`，否则返回 `false`。

尝试一下
----

语法
--

```js
includes(searchElement)
includes(searchElement, fromIndex)
```

### 参数

`searchElement`

需要查找的值。

`fromIndex` 可选

开始搜索的索引（从零开始），会转换为整数。

* 负索引从数组末尾开始计数——如果 `fromIndex < 0`，那么实际使用的是 `fromIndex + array.length`。然而在这种情况下，数组仍然从前往后进行搜索。
* 如果 `fromIndex < -array.length` 或者省略 `fromIndex`，则使用 `0`，这将导致整个数组被搜索。
* 如果 `fromIndex >= array.length`，则不会搜索数组并返回 `false`。

### 返回值

一个布尔值，如果在数组中（或者在 `fromIndex` 所指示的数组部分中，如果指定 `fromIndex` 的话）找到 `searchElement` 值，则该值为 `true`。

描述
--

`includes()` 方法使用零值相等算法将 `searchElement` 与数组中的元素进行比较。0 值都被认为是相等的，不管符号是什么。（即 `-0` 等于 `0`），但 `false` *不*被认为与 `0` 相同。`NaN` 可以被正确搜索到。

当在稀疏数组上使用时，`includes()` 方法迭代空槽，就像它们的值是 `undefined` 一样。

`includes()` 方法是通用的。它只期望 `this` 值具有 `length` 属性和整数键属性。

示例
--

### 使用 includes() 方法

```js
[1, 2, 3].includes(2); // true
[1, 2, 3].includes(4); // false
[1, 2, 3].includes(3, 3); // false
[1, 2, 3].includes(3, -1); // true
[1, 2, NaN].includes(NaN); // true
["1", "2", "3"].includes(3); // false
```

### fromIndex 大于等于数组长度

如果 `fromIndex` 大于等于数组的长度，则将直接返回 `false`，且不搜索该数组。

```js
const arr = ["a", "b", "c"];

arr.includes("c", 3); // false
arr.includes("c", 100); // false
```

### 计算出的索引小于 0

如果 `fromIndex` 为负值，计算出的索引将作为开始搜索 `searchElement` 的位置。如果计算出的索引小于 `0`，则整个数组都会被搜索。

```js
// 数组长度为 3
// fromIndex 为 -100
// 计算出的索引为 3 + (-100) = -97

const arr = ["a", "b", "c"];

arr.includes("a", -100); // true
arr.includes("b", -100); // true
arr.includes("c", -100); // true
arr.includes("a", -2); // false
```

### 对稀疏数组使用 includes() 方法

你可以在稀疏数组中搜索 `undefined`，得到 `true` 。

```js
console.log([1, , 3].includes(undefined)); // true
```

### 在非数组对象上调用 includes() 方法

`includes()` 方法读取 `this` 的 `length` 属性，然后访问每个整数索引。

```js
const arrayLike = {
  length: 3,
  0: 2,
  1: 3,
  2: 4,
};
console.log(Array.prototype.includes.call(arrayLike, 2));
// true
console.log(Array.prototype.includes.call(arrayLike, 1));
// false
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-array.prototype.includes |

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

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

参见
--

* `core-js` 中 `Array.prototype.includes` 的 polyfill
* 索引集合类
* `Array`
* `Array.prototype.indexOf()`
* `Array.prototype.find()`
* `Array.prototype.findIndex()`
* `TypedArray.prototype.includes()`
* `String.prototype.includes()`


Array.prototype.shift() - JavaScript

Array.prototype.shift()
=======================

**`shift()`** 方法从数组中删除**第一个**元素，并返回该元素的值。此方法更改数组的长度。

尝试一下
----

语法
--

```js
shift()
```

### 返回值

从数组中删除的元素；如果数组为空则返回 `undefined`。

描述
--

`shift` 方法移除索引为 0 的元素，并将后续元素的下标依次向前移动，然后返回被移除的元素。如果 `length` 属性的值为 0，则返回 `undefined`。

`pop()` 方法有着和 `shift()` 相似的行为。但是是作用于数组的最后一个元素上的。

`shift()` 方法是一个改变方法。它改变了 `this` 的内容和长度。如果你希望保持 `this` 的值不变，但返回一个删除了第一个元素的新数组，你可以使用 `arr.slice(1)`。

`shift()` 方法是通用的。它只期望 `this` 值具有 `length` 属性和整数键属性。虽然字符串也是类似数组的，但这个方法不适合应用于它们，因为字符串是不可变的。

示例
--

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

以下代码显示了删除其第一个元素之前和之后的 `myFish` 数组。它还显示已删除的元素：

```js
const myFish = ["angel", "clown", "mandarin", "surgeon"];

console.log("调用 shift 之前：", myFish);
// 调用 shift 之前： ['angel', 'clown', 'mandarin', 'surgeon']

const shifted = myFish.shift();

console.log("调用 shift 之后：", myFish);
// 调用 shift 之后： ['clown', 'mandarin', 'surgeon']

console.log("被删除的元素：" + shifted);
// "被删除的元素：angel"
```

### 在 while 循环中使用 shift()

shift() 方法经常用于 while 循环的条件中。下例中每次迭代都会从一个数组中移除下一项元素，直至它成为空数组。

```js
const names = ["Andrew", "Tyrone", "Paul", "Maria", "Gayatri"];

while (typeof (i = names.shift()) !== "undefined") {
  console.log(i);
}
// Andrew, Tyrone, Paul, Maria, Gayatri
```

### 在非数组对象上调用 shift()

`shift` 方法会读取 `this` 的 `length` 属性。如果规范化长度为 0，`length` 再次设置为 `0`（而之前可能为负值或 `undefined`）。否则，返回 `0` 处的属性，其余属性向左移动 1。`length` 属性递减 1。

```js
const arrayLike = {
  length: 3,
  unrelated: "foo",
  2: 4,
};
console.log(Array.prototype.shift.call(arrayLike));
// undefined，因为它是一个空槽
console.log(arrayLike);
// { '1': 4, length: 2, unrelated: 'foo' }

const plainObj = {};
// 这里没有长度属性，所以长度为 0
Array.prototype.shift.call(plainObj);
console.log(plainObj);
// { length: 0 }
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-array.prototype.shift |

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

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

参见
--

* 索引集合类
* `Array`
* `Array.prototype.push()`
* `Array.prototype.pop()`
* `Array.prototype.unshift()`
* `Array.prototype.concat()`
* `Array.prototype.splice()`


Array.prototype[@@iterator]() - JavaScript

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

`Array` 实例的 **`[@@iterator]()`** 方法实现了迭代协议，允许数组被大多数期望可迭代对象的语法所使用，例如展开语法和 `for...of` 循环。它返回一个数组迭代器对象，该对象会产生数组中每个索引的值。

该属性的初始值与 `Array.prototype.values` 属性的初始值是相同的函数对象。

尝试一下
----

语法
--

```js
array[Symbol.iterator]()
```

### 返回值

与 `Array.prototype.values()` 相同的返回值：一个新的可迭代迭代器对象，它会生成数组中每个索引的值。

示例
--

### 使用 for...of 循环进行迭代

请注意，你很少需要直接调用此方法。`@@iterator` 方法的存在使数组可迭代，并且像 `for...of` 循环这样的迭代语法会自动调用此方法以获得要遍历的迭代器。

#### HTML

```html
<ul id="letterResult"></ul>
```

#### JavaScript

```js
const arr = ["a", "b", "c"];
const letterResult = document.getElementById("letterResult");
for (const letter of arr) {
  const li = document.createElement("li");
  li.textContent = letter;
  letterResult.appendChild(li);
}
```

#### 结果

### 手动执行迭代器

你仍然可以手动调用返回迭代器对象的 `next()` 方法，以实现对迭代过程的最大控制。

```js
const arr = ["a", "b", "c", "d", "e"];
const arrIter = arr[Symbol.iterator]();
console.log(arrIter.next().value); // a
console.log(arrIter.next().value); // b
console.log(arrIter.next().value); // c
console.log(arrIter.next().value); // d
console.log(arrIter.next().value); // e
```

### 使用相同的函数处理字符串和字符串数组

因为字符串和数组都实现了可迭代协议，所以可以设计一个通用函数以相同的方式处理这两种输入。这比直接调用 `Array.prototype.values()` 更好，后者要求输入是一个数组，或者至少是一个具有这种方法的对象。

```js
function logIterable(it) {
  if (typeof it[Symbol.iterator] !== "function") {
    console.log(it, "不可迭代。");
    return;
  }
  for (const letter of it) {
    console.log(letter);
  }
}

// 数组
logIterable(["a", "b", "c"]);
// a
// b
// c

// 字符串
logIterable("abc");
// a
// b
// c

// 数值
logIterable(123);
// 123 不可迭代。
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-array.prototype-@@iterator |

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

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

参见
--

* `core-js` 中 `Array.prototype[@@iterator]` 的 polyfill
* 索引集合类
* `Array`
* `Array.prototype.keys()`
* `Array.prototype.entries()`
* `Array.prototype.values()`
* `TypedArray.prototype[@@iterator]()`
* `String.prototype[@@iterator]()`
* `Symbol.iterator`
* 迭代协议


Array.prototype.lastIndexOf() - JavaScript

Array.prototype.lastIndexOf()
=============================

概述
--

**`lastIndexOf()`** 方法返回数组中给定元素最后一次出现的索引，如果不存在则返回 -1。该方法从 `fromIndex` 开始向前搜索数组。

尝试一下
----

语法
--

```js
lastIndexOf(searchElement)
lastIndexOf(searchElement, fromIndex)
```

### 参数

`searchElement`

被查找的元素。

`fromIndex` 可选

以 0 起始的索引，表明反向搜索的起始位置，会被转换为整数。

* 如果 `fromIndex < 0`，则从数组末尾开始倒数计数——即使用 `fromIndex + array.length` 的值。
* 如果 `fromIndex < -array.length`，则不搜索数组并返回 `-1`。从概念上讲，你可以把它想象成从数组开始之前不存在的位置开始反向搜索，这条路径上没有任何数组元素，因此 `searchElement` 永远不会被找到。
* 如果 `fromIndex >= array.length` 或者省略了 `fromIndex`，则使用 `array.length - 1`，这会导致搜索整个数组。可以将其理解为从数组尾部之后不存在的位置开始向前搜索。最终会访问到数组最后一个元素，并继续向前开始实际搜索数组元素。

### 返回值

数组中该元素最后一次出现的索引，如未找到返回 **-1**。

描述
--

`lastIndexOf` 使用严格相等（与 `===` 运算符使用的算法相同）比较 `searchElement` 和数组中的元素。`NaN` 值永远不会被比较为相等，因此当 `searchElement` 为 `NaN` 时 `lastIndexOf()` 总是返回 `-1`。

`lastIndexOf()` 方法会跳过稀疏数组中的空槽。

`lastIndexOf()` 方法是通用的。它只期望 `this` 值具有 `length` 属性和整数键属性。

示例
--

### 使用 lastIndexOf()

下例使用 `lastIndexOf()` 定位数组中的值。

```js
const numbers = [2, 5, 9, 2];
numbers.lastIndexOf(2); // 3
numbers.lastIndexOf(7); // -1
numbers.lastIndexOf(2, 3); // 3
numbers.lastIndexOf(2, 2); // 0
numbers.lastIndexOf(2, -2); // 0
numbers.lastIndexOf(2, -1); // 3
```

你不能用 `lastIndexOf()` 来搜索 `NaN`。

```js
const array = [NaN];
array.lastIndexOf(NaN); // -1
```

### 查找元素出现的所有索引

下例使用 `lastIndexOf` 查找到一个元素在数组中所有的索引（下标），并在找到它们时用 `push` 将它们添加到另一个数组中。

```js
const indices = [];
const array = ["a", "b", "a", "c", "a", "d"];
const element = "a";
let idx = array.lastIndexOf(element);
while (idx !== -1) {
  indices.push(idx);
  idx = idx > 0 ? array.lastIndexOf(element, idx - 1) : -1;
}

console.log(indices);
// [4, 2, 0]
```

需要注意的是，这里必须单独处理 `idx === 0` 的情况，因为如果该元素是数组的第一个元素，则无论 `fromIndex` 参数的值为何，它总是会被找到。这与 `indexOf` 方法不同。

### 在稀疏数组上使用 lastIndexOf()

你不能使用 `lastIndexOf()` 来搜索稀疏数组中的空槽。

```js
console.log([1, , 3].lastIndexOf(undefined)); // -1
```

### 在非数组对象上调用 lastIndexOf()

`lastIndexOf()` 方法读取 `this` 的 `length` 属性，然后访问每个整数索引。

```js
const arrayLike = {
  length: 3,
  0: 2,
  1: 3,
  2: 2,
};
console.log(Array.prototype.lastIndexOf.call(arrayLike, 2));
// 2
console.log(Array.prototype.lastIndexOf.call(arrayLike, 5));
// -1
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-array.prototype.lastindexof |

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

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

参见
--

* `core-js` 中 `Array.prototype.lastIndexOf` 的 polyfill
* 索引集合类
* `Array`
* `Array.prototype.findIndex()`
* `Array.prototype.findLastIndex()`
* `Array.prototype.indexOf()`
* `TypedArray.prototype.lastIndexOf()`
* `String.prototype.lastIndexOf()`


Array.prototype.unshift() - JavaScript

Array.prototype.unshift()
=========================

**`unshift()`** 方法将指定元素添加到数组的开头，并返回数组的新长度。

尝试一下
----

语法
--

```js
unshift()
unshift(element1)
unshift(element1, element2)
unshift(element1, element2, /\* …, \*/ elementN)
```

### 参数

`element1`、…、`elementN`

添加到 `arr` 开头的元素。

### 返回值

返回调用方法对象的新 `length` 属性。

描述
--

`unshift()` 方法将给定的值插入到类数组对象的开头。

`Array.prototype.push()` 有着和 `unshift()` 相似的行为，但是其将元素插入到数组的末尾。

请注意，如果多个元素作为参数传递，它们将被插入到对象开头的块中，与它们作为参数传递的顺序完全相同。因此，调用**一次** `unshift()` 方法并传递 `n` 个参数，与调用 `n` 次并传递 **1** 个参数（例如使用循环），不会产生相同的结果。

请看示例：

```js
let arr = [4, 5, 6];

arr.unshift(1, 2, 3);
console.log(arr);
// [1, 2, 3, 4, 5, 6]

arr = [4, 5, 6]; // 重置数组

arr.unshift(1);
arr.unshift(2);
arr.unshift(3);

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

`unshift()` 方法是通用的。它只期望 `this` 值具有 `length` 属性和整数键属性。虽然字符串也是类似于数组的，但这个方法不适用于它们，因为字符串是不可变的。

示例
--

### 使用 unshift()

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

arr.unshift(0); // 调用的结果是 3，这是新的数组长度。
// 数组是 [0, 1, 2]

arr.unshift(-2, -1); // 新的数组长度是 5
// 数组是 [-2, -1, 0, 1, 2]

arr.unshift([-4, -3]); // 新的数组长度是 6
// 数组是 [[-4, -3], -2, -1, 0, 1, 2]

arr.unshift([-7, -6], [-5]); // 新的数组长度是 8
// 数组是 [ [-7, -6], [-5], [-4, -3], -2, -1, 0, 1, 2 ]
```

### 在非数组对象中使用 unshift()

`unshift` 方法会读取 `this` 的 `length` 属性。然后，它将 `0` 到 `length - 1` 范围内的所有属性按参数数量右移，并将每个索引从 `0` 开始，并将参数传递给 `unshift()`。最后，它将 `length` 设置为之前的长度加上前置元素的数量。

```js
const arrayLike = {
  length: 3,
  unrelated: "foo",
  2: 4,
};
Array.prototype.unshift.call(arrayLike, 1, 2);
console.log(arrayLike);
// { '0': 1, '1': 2, '4': 4, length: 5, unrelated: 'foo' }

const plainObj = {};
// 这里没有长度属性，所以这里的长的为 0
Array.prototype.unshift.call(plainObj, 1, 2);
console.log(plainObj);
// { '0': 1, '1': 2, length: 2 }
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-array.prototype.unshift |

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

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

参见
--

* `core-js` 中 `Array.prototype.unshift` 的 Polyfill，并对此方法进行了修复
* 索引集合类
* `Array`
* `Array.prototype.push()`
* `Array.prototype.pop()`
* `Array.prototype.shift()`
* `Array.prototype.concat()`
* `Array.prototype.splice()`


Array() 构造函数 - JavaScript

Array() 构造函数
============

**`Array()`** 构造函数用于创建 `Array` 对象。

语法
--

```js
new Array()
new Array(element0)
new Array(element0, element1)
new Array(element0, element1, /\* … ,\*/ elementN)
new Array(arrayLength)

Array()
Array(element0)
Array(element0, element1)
Array(element0, element1, /\* … ,\*/ elementN)
Array(arrayLength)
```

**备注：** 调用 `Array()` 时可以使用或不使用 `new`。两者都会创建一个新的 `Array` 实例。

### 参数

`elementN`

`Array` 构造函数会根据给定的元素创建一个 JavaScript 数组，但是当仅有一个参数且为数字时除外（详见下面的 `arrayLength` 参数）。注意，后者仅适用于用 Array 构造函数创建数组，而不适用于用方括号创建的数组字面量。

`arrayLength`

如果传递给 `Array` 构造函数的唯一参数是介于 0 到 232 - 1（含）之间的整数，这将返回一个新的 JavaScript 数组，其 `length` 属性设置为该数字（**注意**：这意味着一个由 `arrayLength` 个空槽组成的数组，而不是具有实际 `undefined` 值的槽——参见稀疏数组）。

### 异常

`RangeError`

如果只有一个参数（`arrayLength`）且其值不在 0 到 232 - 1（包括）之间，则会触发异常。

示例
--

### 数组字面量

可以通过使用数组字面量创建数组：

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

console.log(fruits.length); // 2
console.log(fruits[0]); // "Apple"
```

### 单个参数的 Array 构造函数

可以通过单个数字参数的构造函数创建数组，数组的长度为传入的参数，该数组不包含任何实际的元素。

```js
const arrayEmpty = new Array(2);

console.log(arrayEmpty.length); // 2
console.log(arrayEmpty[0]); // undefined；实际上是一个空槽
console.log(0 in arrayEmpty); // false
console.log(1 in arrayEmpty); // false
```

```js
const arrayOfOne = new Array("2"); // 这里是字符串 "2" 而不是数字 2

console.log(arrayOfOne.length); // 1
console.log(arrayOfOne[0]); // "2"
```

### 多个参数的 Array 构造函数

如果向构造函数传入多个参数，则会创建一个包含给定元素的新 `Array`。

```js
const fruits = new Array("Apple", "Banana");

console.log(fruits.length); // 2
console.log(fruits[0]); // "Apple"
```

规范
--

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

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

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

参见
--

* 索引集合类
* `Array`


Array.prototype.reduceRight() - JavaScript

Array.prototype.reduceRight()
=============================

**`reduceRight()`** 方法对累加器（accumulator）和数组的每个值（按从右到左的顺序）应用一个函数，并使其成为单个值。

对于从左至右遍历的相似方法，请参阅 `Array.prototype.reduce()`。

尝试一下
----

语法
--

```js
reduceRight(callbackFn)
reduceRight(callbackFn, initialValue)
```

### 参数

`callbackFn`

为数组中的每个元素执行的函数。其返回值将作为下一次调用 `callbackFn` 时的 `accumulator` 参数。对于最后一次调用，返回值将成为 `reduceRight()` 的返回值。该函数被调用时将传入以下参数：

`accumulator`

上一次调用 `callbackFn` 的结果。在第一次调用时，如果指定了 `initialValue` 则为指定的值，否则为数组最后一个元素的值。

`currentValue`

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

`index`

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

`array`

调用了 `reduceRight()` 的数组本身。

`initialValue` 可选

首次调用 `callbackFn` 时累加器的值。如果不提供初始值，则将使用数组中的最后一个元素，并在迭代时跳过它。没有初始值的情况下，在空数组上调用 `reduceRight()` 会产生 `TypeError`。

### 返回值

聚合后的结果值。

描述
--

`reduceRight()` 方法是一个迭代方法。它为数组中的所有元素降序调用“reducer”回调函数，并将它们累积到一个单一的值中。

`callbackFn` 仅为已分配值的数组索引调用。它不会为稀疏数组中的空槽调用。

与其他迭代方法不同，`reduceRight()` 不接受 `thisArg` 参数。`callbackFn` 调用时始终以 `undefined` 作为 `this` 的值，如果 `callbackFn` 未处于严格模式，则该值将被替换为 `globalThis`。

`reduceRight()` 不会改变被调用的数组，但是作为 `callbackFn` 提供的函数可能会改变数组。但需要注意的是，在第一次调用 `callbackFn` *之前*，数组的长度会被保存。因此：

* 当开始调用 `reduceRight()` 时，`callbackFn` 将不会访问超出数组初始长度的任何元素。
* 对已访问索引的更改不会导致再次在这些元素上调用 `callbackFn`。
* 如果数组中一个现有的、尚未访问的元素被 `callbackFn` 更改，则它传递给 `callbackFn` 的值将是该元素被修改后的值。被删除的元素则不会被访问。

**警告：** 上述类型的并发修改经常导致难以理解的代码，通常应避免（特殊情况除外）。

`reduceRight()` 方法是通用的。它只期望 `this` 值具有 `length` 属性和整数键属性。

示例
--

### 无初始值时 reduceRight() 如何运行

`reduceRight()` 方法调用 `callbackFn` 的格式如下：

```js
arr.reduceRight((accumulator, currentValue, index, array) => {
  // …
});
```

在第一次调用函数时，`accumulator` 和 `currentValue` 的可能取值情况有两种。如果在调用 `reduceRight` 时提供了 `initialValue`，那么 `accumulator` 将等于 `initialValue`，而 `currentValue` 将等于数组中的最后一个值。如果没有提供 `initialValue`，则 `accumulator` 将等于数组中的最后一个值，而 `currentValue` 将等于倒数第二个值。

如果数组为空且没有提供 `initialValue`，则会抛出 `TypeError` 异常。如果数组只有一个元素（无论其位置如何）且没有提供 `initialValue`，或者提供了 `initialValue` 但数组为空，则直接返回该单个值，且 `callbackFn` 不会被调用。

这个函数的一些示例运行如下：

```js
[0, 1, 2, 3, 4].reduceRight(
  (accumulator, currentValue, index, array) => accumulator + currentValue,
);
```

一共会调用四次回调函数，每次调用的参数及返回值如下：

|  | `accumulator` | `currentValue` | `index` | 返回值 |
| --- | --- | --- | --- | --- |
| 第一次调用 | `4` | `3` | `3` | `7` |
| 第二次调用 | `7` | `2` | `2` | `9` |
| 第三次调用 | `9` | `1` | `1` | `10` |
| 第四次调用 | `10` | `0` | `0` | `10` |

`array` 参数在整个过程中始终不变，始终为 `[0, 1, 2, 3, 4]`。`reduceRight` 返回的值将是最后一次回调函数调用的返回值（`10`）。

### 有初始值时 reduceRight() 如何运行

这里我们使用相同的算法对同一数组进行 reduce，但是将 `initialValue` 参数设置为 `10`，作为第二个参数传递给 `reduceRight()` 方法：

```js
[0, 1, 2, 3, 4].reduceRight(
  (accumulator, currentValue, index, array) => accumulator + currentValue,
  10,
);
```

|  | `accumulator` | `currentValue` | `index` | 返回值 |
| --- | --- | --- | --- | --- |
| 第一次调用 | `10` | `4` | `4` | `14` |
| 第二次调用 | `14` | `3` | `3` | `17` |
| 第三次调用 | `17` | `2` | `2` | `19` |
| 第四次调用 | `19` | `1` | `1` | `20` |
| 第五次调用 | `20` | `0` | `0` | `20` |

这次，`reduceRight` 返回值为 `20`。

### 求一个数组中所有值的和

```js
const sum = [0, 1, 2, 3].reduceRight((a, b) => a + b);
// sum 的值是 6
```

### 展平一个二维数组

```js
const arrays = [
  [0, 1],
  [2, 3],
  [4, 5],
];
const flattened = arrays.reduceRight((a, b) => a.concat(b), []);
// flattened 的值是 [4, 5, 2, 3, 0, 1]
```

### 串联运行一列异步函数，每个函数都将其结果传给下一个函数

```js
const waterfall =
  (...functions) =>
  (callback, ...args) =>
    functions.reduceRight(
      (composition, fn) =>
        (...results) =>
          fn(composition, ...results),
      callback,
    )(...args);

const randInt = (max) => Math.floor(Math.random() \* max);

const add5 = (callback, x) => {
  setTimeout(callback, randInt(1000), x + 5);
};
const mult3 = (callback, x) => {
  setTimeout(callback, randInt(1000), x \* 3);
};
const sub2 = (callback, x) => {
  setTimeout(callback, randInt(1000), x - 2);
};
const split = (callback, x) => {
  setTimeout(callback, randInt(1000), x, x);
};
const add = (callback, x, y) => {
  setTimeout(callback, randInt(1000), x + y);
};
const div4 = (callback, x) => {
  setTimeout(callback, randInt(1000), x / 4);
};

const computation = waterfall(add5, mult3, sub2, split, add, div4);
computation(console.log, 5); // Logs 14

// same as:

const computation2 = (input, callback) => {
  const f6 = (x) => div4(callback, x);
  const f5 = (x, y) => add(f6, x, y);
  const f4 = (x) => split(f5, x);
  const f3 = (x) => sub2(f4, x);
  const f2 = (x) => mult3(f3, x);
  add5(f2, input);
};
```

### `reduce` 与 `reduceRight` 之间的区别

```js
const a = ["1", "2", "3", "4", "5"];
const left = a.reduce((prev, cur) => prev + cur);
const right = a.reduceRight((prev, cur) => prev + cur);

console.log(left); // "12345"
console.log(right); // "54321"
```

### 定义可组合函数

函数组合是一种将函数组合在一起的机制，其中每个函数的输出都作为下一个函数的输入，最后一个函数的输出是最终的结果。在这个例子中，我们使用 `reduceRight()` 来实现函数组合。

参见维基百科上的函数组合。

```js
const compose =
  (...args) =>
  (value) =>
    args.reduceRight((acc, fn) => fn(acc), value);

// Increment passed number
const inc = (n) => n + 1;

// Doubles the passed value
const double = (n) => n \* 2;

// using composition function
console.log(compose(double, inc)(2)); // 6

// using composition function
console.log(compose(inc, double)(2)); // 5
```

### 在稀疏数组中使用 reduceRight()

`reduceRight()` 会跳过稀疏数组中缺失的元素，但不会跳过 `undefined` 值。

```js
console.log([1, 2, , 4].reduceRight((a, b) => a + b)); // 7
console.log([1, 2, undefined, 4].reduceRight((a, b) => a + b)); // NaN
```

### 在非数组对象上调用 reduceRight()

`reduceRight()` 方法读取 `this` 的 `length` 属性，然后访问每个整数索引。

```js
const arrayLike = {
  length: 3,
  0: 2,
  1: 3,
  2: 4,
};
console.log(Array.prototype.reduceRight.call(arrayLike, (x, y) => x - y));
// -1, 即 4 - 3 - 2
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-array.prototype.reduceright |

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

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

参见
--

* `core-js` 中 `Array.prototype.reduce` 的 polyfill
* 索引集合类
* `Array`
* `Array.prototype.map()`
* `Array.prototype.flat()`
* `Array.prototype.flatMap()`
* `Array.prototype.reduce()`
* `TypedArray.prototype.reduceRight()`
* `Object.groupBy()`
* `Map.groupBy()`


Array.from() - JavaScript

Array.from()
============

**`Array.from()`** 静态方法从可迭代或类数组对象创建一个新的浅拷贝的数组实例。

转换异步的可迭代对象到数组，可以使用 `Array.fromAsync()`。

尝试一下
----

语法
--

```js
Array.from(arrayLike)
Array.from(arrayLike, mapFn)
Array.from(arrayLike, mapFn, thisArg)
```

### 参数

`arrayLike`

想要转换成数组的类数组或可迭代对象。

`mapFn` 可选

调用数组每个元素的函数。如果提供，每个将要添加到数组中的值首先会传递给该函数，然后将 `mapFn` 的返回值增加到数组中。使用以下参数调用该函数：

`element`

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

`index`

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

`thisArg` 可选

执行 `mapFn` 时用作 `this` 的值。

### 返回值

一个新的数组实例。

描述
--

`Array.from()` 可以通过以下方式来创建数组对象：

* 可迭代对象（例如 `Map` 和 `Set` 对象）；或者，如果对象是不可迭代的，
* 类数组对象（带有 `length` 属性和索引元素的对象）。

`Array.from()` 绝不会创建稀疏数组。如果 `arrayLike` 对象缺少一些索引属性，那么这些属性在新数组中将是 `undefined`。

`Array.from()` 有一个可选的参数 `mapFn`，该参数允许你在创建数组时为每个元素执行一个函数，类似于 `map()`。更明确地说，`Array.from(obj, mapFn, thisArg)` 和 `Array.from(obj).map(mapFn, thisArg)` 具有相同的结果，只是它不会创建中间数组，并且 `mapFn` 仅接受两个参数（`element`、`index`），不接受数组，因为数组仍然在构建中。

**备注：** 此行为对于类型化数组更为重要，因为中间数组的值必须被截断，以适应相应的类型。`Array.from()` 的实现与 `TypedArray.from()` 具有相同的签名。

`Array.from()` 方法是一个通用的工厂方法。例如，如果一个数组类的子类继承 `from()` 方法，继承的 `from()` 方法将返回新的子类的实例，而不是数组的实例。事实上，`this` 值可以是任意的构造函数，只要该构造函数接受一个表示新数组长度的单个参数。当一个迭代器对象作为类数组传递时，不带参数调用构造函数；当传递类数组对象时，将携带类数组对象的规范化长度调用构造函数。迭代完成时，将再次设置最终的 `length`。如果 `this` 值并不是构造函数，则使用 `Array` 构造函数。

示例
--

### 从字符串构建数组

```js
Array.from("foo");
// [ "f", "o", "o" ]
```

### 从 Set 构建数组

```js
const set = new Set(["foo", "bar", "baz", "foo"]);
Array.from(set);
// [ "foo", "bar", "baz" ]
```

### 从 Map 构建数组

```js
const map = new Map([
  [1, 2],
  [2, 4],
  [4, 8],
]);
Array.from(map);
// [[1, 2], [2, 4], [4, 8]]

const mapper = new Map([
  ["1", "a"],
  ["2", "b"],
]);
Array.from(mapper.values());
// ['a', 'b'];

Array.from(mapper.keys());
// ['1', '2'];
```

### 从 NodeList 构建数组

```js
// 根据 DOM 元素的属性创建一个数组
const images = document.querySelectorAll("img");
const sources = Array.from(images, (image) => image.src);
const insecureSources = sources.filter((link) => link.startsWith("http://"));
```

### 从类数组对象构建数组（arguments）

```js
function f() {
  return Array.from(arguments);
}
f(1, 2, 3);
// [ 1, 2, 3 ]
```

### 使用箭头函数和 Array.from()

```js
// 使用箭头函数作为映射函数去操作多个元素
Array.from([1, 2, 3], (x) => x + x);
// [2, 4, 6]

// 生成一个数字序列。因为数组在每个位置都使用 `undefined` 初始化，下面的 `v` 值将是 `undefined`
Array.from({ length: 5 }, (v, i) => i);
// [0, 1, 2, 3, 4]
```

### 序列生成器（range）

```js
// 序列生成器函数（通常称为“range”，例如 Clojure、PHP 等）
const range = (start, stop, step) =>
  Array.from({ length: (stop - start) / step + 1 }, (\_, i) => start + i \* step);

// 生成的数字范围 0..4
range(0, 4, 1);
// [0, 1, 2, 3, 4]

// 生成的数字范围 1..10，步长为 2
range(1, 10, 2);
// [1, 3, 5, 7, 9]

// 使用 Array.from 生成字母表，并将其序列排序
range("A".charCodeAt(0), "Z".charCodeAt(0), 1).map((x) =>
  String.fromCharCode(x),
);
// ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]
```

### 在非数组构造函数上调用 from()

`from()` 方法可以在任何构造函数上调用，只要该构造函数接受一个表示新数组长度的单个参数。

```js
function NotArray(len) {
  console.log("NotArray called with length", len);
}

// 可迭代对象
console.log(Array.from.call(NotArray, new Set(["foo", "bar", "baz"])));
// NotArray called with length undefined
// NotArray { '0': 'foo', '1': 'bar', '2': 'baz', length: 3 }

// 类数组对象
console.log(Array.from.call(NotArray, { length: 1, 0: "foo" }));
// NotArray called with length 1
// NotArray { '0': 'foo', length: 1 }
```

当 `this` 值不是构造函数，返回一个普通的数组对象。

```js
console.log(Array.from.call({}, { length: 1, 0: "foo" })); // [ 'foo' ]
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-array.from |

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

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

参见
--

* `core-js` 中的 `Array.from` Polyfill
* 索引集合类
* `Array`
* `Array()`
* `Array.of()`
* `Array.fromAsync()`
* `Array.prototype.map()`
* `TypedArray.from()`


Array.prototype.flat() - JavaScript

Array.prototype.flat()
======================

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

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

* Learn more
* See full compatibility
* Report feedback
**`flat()`** 方法创建一个新的数组，并根据指定深度递归地将所有子数组元素拼接到新的数组中。

尝试一下
----

语法
--

```js
flat()
flat(depth)
```

### 参数

`depth` 可选

指定要提取嵌套数组的结构深度，默认值为 1。

### 返回值

一个新的数组，其中包含拼接后的子数组元素。

描述
--

`flat()` 方法属于复制方法。它不会改变 `this` 数组，而是返回一个浅拷贝，该浅拷贝包含了原始数组中相同的元素。

如果待展开的数组是稀疏的，`flat()` 方法会忽略其中的空槽。例如，如果 `depth` 是 1，那么根数组和第一层嵌套数组中的空槽都会被忽略，但在更深的嵌套数组中的空槽则会与这些数组一起保留。

`flat()` 方法是通用的。它只需要 `this` 值具有 `length` 属性和整数键属性即可。但是，如果要展开元素，则它们必须是数组。

示例
--

### 展平嵌套数组

```js
const arr1 = [1, 2, [3, 4]];
arr1.flat();
// [1, 2, 3, 4]

const arr2 = [1, 2, [3, 4, [5, 6]]];
arr2.flat();
// [1, 2, 3, 4, [5, 6]]

const arr3 = [1, 2, [3, 4, [5, 6]]];
arr3.flat(2);
// [1, 2, 3, 4, 5, 6]

const arr4 = [1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]];
arr4.flat(Infinity);
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
```

### 在稀疏数组上使用 flat()

`flat()` 方法删除数组中的空槽：

```js
const arr5 = [1, 2, , 4, 5];
console.log(arr5.flat()); // [1, 2, 4, 5]

const array = [1, , 3, ["a", , "c"]];
console.log(array.flat()); // [ 1, 3, "a", "c" ]

const array2 = [1, , 3, ["a", , ["d", , "e"]]];
console.log(array2.flat()); // [ 1, 3, "a", ["d", empty, "e"] ]
console.log(array2.flat(2)); // [ 1, 3, "a", "d", "e"]
```

### 在非数组对象上调用 flat()

`flat()` 方法读取 `this` 的 `length` 属性，然后访问每个整数索引。如果元素不是数组，则直接将其附加到结果中。如果元素是数组，则根据 `depth` 参数进行展开操作。

```js
const arrayLike = {
  length: 3,
  0: [1, 2],
  // 嵌套的类数组对象不会被展平
  1: { length: 2, 0: 3, 1: 4 },
  2: 5,
};
console.log(Array.prototype.flat.call(arrayLike));
// [ 1, 2, { '0': 3, '1': 4, length: 2 }, 5 ]
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-array.prototype.flat |

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

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

参见
--

* `core-js` 中 `Array.prototype.flat` 的 polyfill
* 索引集合类
* `Array`
* `Array.prototype.concat()`
* `Array.prototype.flatMap()`
* `Array.prototype.map()`
* `Array.prototype.reduce()`


Array.prototype.toReversed() - JavaScript

Array.prototype.toReversed()
============================

`Array` 实例的 **`toReversed()`** 方法是 `reverse()` 方法对应的复制版本。它返回一个元素顺序相反的新数组。

语法
--

```js
toReversed()
```

### 返回值

一个包含以相反顺序排列元素的新数组。

描述
--

`toReversed()` 方法将调用该方法的数组对象的元素以相反的顺序调换，并返回一个新数组。

当用于稀疏数组时，`toReversed()` 方法迭代空槽，就像它们的值是 `undefined` 一样。

`toReversed()` 方法是通用的。它只期望 `this` 值具有 `length` 属性和整数键属性。

示例
--

### 反转数组中的元素

下面的示例创建了一个包含三个元素的数组 `items`，然后创建了一个新的数组，该数组是 `items` 的反转。`items` 数组保持不变。

```js
const items = [1, 2, 3];
console.log(items); // [1, 2, 3]

const reversedItems = items.toReversed();
console.log(reversedItems); // [3, 2, 1]
console.log(items); // [1, 2, 3]
```

### 在稀疏数组上使用 toReversed()

`toReversed()` 的返回值永远不是稀疏的。空槽在返回的数组中变为 `undefined`。

```js
console.log([1, , 3].toReversed()); // [3, undefined, 1]
console.log([1, , 3, 4].toReversed()); // [4, 3, undefined, 1]
```

### 在非数组对象上调用 toReversed()

`toReversed()` 方法读取 `this` 的 `length` 属性。然后按降序访问 `length - 1` 和 `0` 之间的每个索引，并将原始数组中该索引的值添加到新数组相应的索引中。

```js
const arrayLike = {
  length: 3,
  unrelated: "foo",
  2: 4,
};
console.log(Array.prototype.toReversed.call(arrayLike));
// [4, undefined, undefined]
// '0' 和 '1' 两个索引不存在，所以它们会变成 undefined
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-array.prototype.toreversed |

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

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

参见
--

* `core-js` 中 `Array.prototype.toReversed` 的 polyfill
* `Array.prototype.reverse()`
* `Array.prototype.toSorted()`
* `Array.prototype.toSpliced()`
* `Array.prototype.with()`
* `TypedArray.prototype.toReversed()` (en-US)


Array.prototype.forEach() - JavaScript

Array.prototype.forEach()
=========================

**`forEach()`** 方法对数组的每个元素执行一次给定的函数。

尝试一下
----

语法
--

```js
forEach(callbackFn)
forEach(callbackFn, thisArg)
```

### 参数

`callbackFn`

为数组中每个元素执行的函数。并会丢弃它的返回值。该函数被调用时将传入以下参数：

`element`

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

`index`

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

`array`

调用了 `forEach()` 的数组本身。

`thisArg` 可选

执行 `callbackFn` 时用作 `this` 的值。参见迭代方法。

### 返回值

`undefined`。

描述
--

`forEach()` 方法是一个迭代方法。它按索引升序地为数组中的每个元素调用一次提供的 `callbackFn` 函数。与 `map()` 不同，`forEach()` 总是返回 `undefined`，而且不能继续链式调用。其典型的用法是在链式调用的末尾执行某些操作。

`callbackFn` 仅对已赋值的数组索引调用。对于稀疏数组中的空槽，它不会被调用。

`forEach()` 不会改变其调用的数组，但是，作为 `callbackFn` 的函数可以更改数组。请注意，在第一次调用 `callbackFn` *之前*，数组的长度已经被保存。因此：

* 当调用 `forEach()` 时，`callbackFn` 不会访问超出数组初始长度的任何元素。
* 已经访问过的索引的更改不会导致 `callbackFn` 再次调用它们。
* 如果 `callbackFn` 更改了数组中已经存在但尚未访问的元素，则传递给 `callbackFn` 的值将是在访问该元素时的值。已经被删除的元素不会被访问。

**警告：** 上述类型的并发修改经常导致难以理解的代码，通常应避免（特殊情况除外）。

`forEach()` 方法是通用的。它只期望 `this` 值具有 `length` 属性和整数键的属性。

除非抛出异常，否则没有办法停止或中断 `forEach()` 循环。如果有这样的需求，则不应该使用 `forEach()` 方法。

可以通过像 `for`、`for...of` 和 `for...in` 这样的循环语句来实现提前终止。当不需要进一步迭代时，诸如 `every()`、`some()`、`find()` 和 `findIndex()` 等数组方法也会立即停止迭代。

`forEach()` 期望的是一个同步函数，它不会等待 Promise 兑现。在使用 Promise（或异步函数）作为 `forEach` 回调时，请确保你意识到这一点可能带来的影响。

```js
const ratings = [5, 4, 5];
let sum = 0;

const sumFunction = async (a, b) => a + b;

ratings.forEach(async (rating) => {
  sum = await sumFunction(sum, rating);
});

console.log(sum);
// 期望的输出：14
// 实际的输出：0
```

如果希望按顺序的或者并发的执行一系列操作，可以查看 promise 组合。

示例
--

### 在稀疏数组上使用 forEach()

```js
const arraySparse = [1, 3, /\* empty \*/, 7];
let numCallbackRuns = 0;

arraySparse.forEach((element) => {
  console.log({ element });
  numCallbackRuns++;
});

console.log({ numCallbackRuns });

// { element: 1 }
// { element: 3 }
// { element: 7 }
// { numCallbackRuns: 3 }
```

如你所见，不会为索引 2 处的缺失值调用回调函数。

### 将 for 循环转换为 forEach

```js
const items = ["item1", "item2", "item3"];
const copyItems = [];

// before
for (let i = 0; i < items.length; i++) {
  copyItems.push(items[i]);
}

// after
items.forEach((item) => {
  copyItems.push(item);
});
```

### 打印出数组的内容

**备注：** 为了在控制台中显示数组的内容，你可以使用 `console.table()` 来展示经过格式化的数组。下面的例子则是另一种使用 `forEach()` 的格式化的方法。

下面的代码会为每一个数组元素输出一行记录：

```js
const logArrayElements = (element, index /\*, array \*/) => {
  console.log(`a[${index}] = ${element}`);
};

// 注意，索引 2 被跳过，因为数组中这个位置没有内容
[2, 5, , 9].forEach(logArrayElements);
// logs:
// a[0] = 2
// a[1] = 5
// a[3] = 9
```

### 使用 thisArg

举个勉强的例子，按照每个数组中的元素值，更新一个对象的属性：

```js
class Counter {
  constructor() {
    this.sum = 0;
    this.count = 0;
  }
  add(array) {
    // 只有函数表达式才有自己的 this 绑定
    array.forEach(function countEntry(entry) {
      this.sum += entry;
      ++this.count;
    }, this);
  }
}

const obj = new Counter();
obj.add([2, 5, 9]);
console.log(obj.count); // 3
console.log(obj.sum); // 16
```

因为 `thisArg` 参数（`this`）传给了 `forEach()`，每次调用时，它都被传给 `callbackFn` 函数，作为它的 `this` 值。

**备注：** 如果使用箭头函数表达式来传入函数参数， `thisArg` 参数会被忽略，因为箭头函数在词法上绑定了 `this` 值。

### 对象复制函数

下面的代码会创建一个给定对象的副本。创建对象的副本有不同的方法，以下是只是一种方法，并解释了 `Array.prototype.forEach()` 是如何使用 `Object.*` 实用工具函数。

```js
const copy = (obj) => {
  const copy = Object.create(Object.getPrototypeOf(obj));
  const propNames = Object.getOwnPropertyNames(obj);
  propNames.forEach((name) => {
    const desc = Object.getOwnPropertyDescriptor(obj, name);
    Object.defineProperty(copy, name, desc);
  });
  return copy;
};

const obj1 = { a: 1, b: 2 };
const obj2 = copy(obj1); // 现在 obj2 看起来和 obj1 一模一样了
```

### 在迭代期间修改数组

下面的例子会输出 `one`, `two`, `four`。

当到达包含值 `two` 的项时，整个数组的第一个项被移除了，这导致所有剩下的项上移一个位置。因为元素 `four` 正位于在数组更前的位置，所以 `three` 会被跳过。

`forEach()` 不会在迭代之前创建数组的副本。

```js
const words = ["one", "two", "three", "four"];
words.forEach((word) => {
  console.log(word);
  if (word === "two") {
    words.shift(); //'one' 将从数组中删除
  }
}); // one // two // four

console.log(words); // ['two', 'three', 'four']
```

### 扁平化数组

下面的示例仅用于学习目的。如果你想使用内置方法来扁平化数组，你可以考虑使用 `Array.prototype.flat()`。

```js
const flatten = (arr) => {
  const result = [];
  arr.forEach((item) => {
    if (Array.isArray(item)) {
      result.push(...flatten(item));
    } else {
      result.push(item);
    }
  });
  return result;
};

// 用例
const nested = [1, 2, 3, [4, 5, [6, 7], 8, 9]];
console.log(flatten(nested)); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
```

### 在非数组对象上调用 forEach()

`forEach()` 方法读取 `this` 的 `length` 属性，然后访问每个整数索引。

```js
const arrayLike = {
  length: 3,
  0: 2,
  1: 3,
  2: 4,
};
Array.prototype.forEach.call(arrayLike, (x) => console.log(x));
// 2
// 3
// 4
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-array.prototype.foreach |

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

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

参见
--

* `core-js` 中 `Array.prototype.forEach` 的 polyfill
* 索引集合类
* `Array`
* `Array.prototype.find()`
* `Array.prototype.map()`
* `Array.prototype.filter()`
* `Array.prototype.every()`
* `Array.prototype.some()`
* `TypedArray.prototype.forEach()`
* `Map.prototype.forEach()`
* `Set.prototype.forEach()`


Array[@@species] - JavaScript

Array[@@species]
================

**`Array[@@species]`** 是一个静态访问器属性，用于返回构造函数，构造函数用于构造数组方法返回值。

**警告：** `@@species` 的存在允许执行任意代码，这可能会产生安全漏洞。它还会使某些优化变得更加困难。引擎开发者正在调查是否要移除此特性。如果可能，请避免依赖它。现代数组方法，如 `toReversed()`，不使用 `@@species` 且始终返回一个新的 `Array` 基类实例。

语法
--

```js
Array[Symbol.species]
```

### 返回值

`get @@species` 被调用的构造函数 (`this`) 的值。该返回值用于构造创建新数组的数组方法的返回值。

描述
--

`@@species` 访问器属性返回 `Array` 对象的默认构造函数。子类的构造函数可能会覆盖并改变构造函数的赋值。默认实现基本上是这样的：

```js
// 以下是一个用于说明的假设底层实现
class Array {
  static get [Symbol.species]() {
    return this;
  }
}
```

由于这种多态实现，派生子类的 `@@species` 默认情况下也会返回构造函数本身。

```js
class SubArray extends Array {}
SubArray[Symbol.species] === SubArray; // true
```

调用不会改变现有数组但会返回新数组实例的数组方法时（例如 `filter()` 和 `map()`），将访问数组的 `constructor[@@species]`。返回的构造函数将用于构造数组方法的返回值。这使得在技术上使数组方法返回与数组无关的对象成为可能。

```js
class NotAnArray {
  constructor(length) {
    this.length = length;
  }
}

const arr = [0, 1, 2];
arr.constructor = { [Symbol.species]: NotAnArray };
arr.map((i) => i); // NotAnArray { '0': 0, '1': 1, '2': 2, length: 3 }
arr.filter((i) => i); // NotAnArray { '0': 1, '1': 2, length: 0 }
arr.concat([1, 2]); // NotAnArray { '0': 0, '1': 1, '2': 2, '3': 1, '4': 2, length: 5 }
```

示例
--

### 普通对象中的 species

`@@species` 属性返回默认构造函数，对于 `Array` 来说，它就是 `Array` 构造函数。

```js
Array[Symbol.species]; // [Function: Array]
```

### 派生对象中的 species

在自定义 `Array` 子类的实例中（例如 `MyArray`），`MyArray` 的 species 是 `MyArray` 构造函数。不过，你可能想要重写这个方法，以便在派生类方法中返回父级 `Array` 对象：

```js
class MyArray extends Array {
  // 重写 MyArray 的 species 属性到父类 Array 的构造函数
  static get [Symbol.species]() {
    return Array;
  }
}
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-get-array-@@species |

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

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

参见
--

* `Array[Symbol.species]` 的 Polyfill 和 `core-js` 中所有受影响的 `Array` 方法对 `Symbol.species` 的支持
* 索引集合类
* `Array`
* `Symbol.species`


Array.prototype.indexOf() - JavaScript

Array.prototype.indexOf()
=========================

**`indexOf()`** 方法返回数组中第一次出现给定元素的下标，如果不存在则返回 -1。

尝试一下
----

语法
--

```js
indexOf(searchElement)
indexOf(searchElement, fromIndex)
```

### 参数

`searchElement`

数组中要查找的元素。

`fromIndex` 可选

开始搜索的索引（从零开始），会转换为整数。

* 负索引从数组末尾开始计数——如果 `frommindex < 0`，使用 `frommindex + array.length`。注意，在这种情况下，仍然从前到后搜索数组。
* 如果 `fromIndex < -array.length` 或者省略了 `fromIndex` ，将使用 `0`，而导致整个数组被搜索。
* 如果 `fromIndex >= array.length`，数组不会继续搜索并返回 `-1`。

### 返回值

首个被找到的元素在数组中的索引位置; 若没有找到则返回 **-1**。

描述
--

`indexOf()` 使用严格相等（与 `===` 运算符使用的算法相同）将 `searchElement` 与数组中的元素进行比较。`NaN` 值永远不会被比较为相等，因此当 `searchElement` 为 `NaN` 时 `indexOf()` 总是返回 `-1`。

`indexOf()` 方法会跳过稀疏数组中的空槽。

`indexOf()` 方法是通用的。它只期望 `this` 值具有 `length` 属性和整数键属性。

示例
--

### 使用 indexOf()

以下例子使用 `indexOf()` 方法确定多个值在数组中的位置。

```js
const array = [2, 9, 9];
array.indexOf(2); // 0
array.indexOf(7); // -1
array.indexOf(9, 2); // 2
array.indexOf(2, -1); // -1
array.indexOf(2, -3); // 0
```

你没法使用 `indexOf()` 来搜索 `NaN`。

```js
const array = [NaN];
array.indexOf(NaN); // -1
```

### 找出指定元素出现的所有位置

```js
const indices = [];
const array = ["a", "b", "a", "c", "a", "d"];
const element = "a";
let idx = array.indexOf(element);
while (idx !== -1) {
  indices.push(idx);
  idx = array.indexOf(element, idx + 1);
}
console.log(indices);
// [0, 2, 4]
```

### 判断一个元素是否在数组里，不在则更新数组

```js
function updateVegetablesCollection(veggies, veggie) {
  if (veggies.indexOf(veggie) === -1) {
    veggies.push(veggie);
    console.log(`New veggies collection is: ${veggies}`);
  } else {
    console.log(`${veggie} already exists in the veggies collection.`);
  }
}

const veggies = ["potato", "tomato", "chillies", "green-pepper"];

updateVegetablesCollection(veggies, "spinach");
// New veggies collection is: potato,tomato,chillies,green-pepper,spinach
updateVegetablesCollection(veggies, "spinach");
// spinach already exists in the veggies collection.
```

### 在稀疏数组中使用 indexOf()

不能使用 `indexOf()` 在稀疏数组中搜索空槽。

```js
console.log([1, , 3].indexOf(undefined)); // -1
```

### 在非数组对象上调用 indexOf()

`indexOf()` 方法读取 `this` 的 `length` 属性，然后访问每个整数索引。

```js
const arrayLike = {
  length: 3,
  0: 2,
  1: 3,
  2: 4,
};
console.log(Array.prototype.indexOf.call(arrayLike, 2));
// 0
console.log(Array.prototype.indexOf.call(arrayLike, 5));
// -1
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-array.prototype.indexof |

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

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

参见
--

* `core-js` 中 `Array.prototype.indexOf` 的 polyfill
* 索引集合类
* `Array`
* `Array.prototype.findIndex()`
* `Array.prototype.findLastIndex()`
* `Array.prototype.lastIndexOf()`
* `TypedArray.prototype.indexOf()`
* `String.prototype.indexOf()`


Array.prototype.findIndex() - JavaScript

Array.prototype.findIndex()
===========================

**`findIndex()`** 方法返回数组中满足提供的测试函数的第一个元素的索引。若没有找到对应元素则返回 -1。

另请参阅 `find()` 方法，它返回满足测试函数的第一个元素（而不是它的索引）。

尝试一下
----

语法
--

```js
findIndex(callbackFn)
findIndex(callbackFn, thisArg)
```

### 参数

`callbackFn`

为数组中的每个元素执行的函数。它应该返回一个真值以指示已找到匹配元素，否则返回一个假值。该函数被调用时将传入以下参数：

`element`

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

`index`

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

`array`

调用了 `findIndex()` 的数组本身。

`thisArg` 可选

执行 `callbackFn` 时用作 `this` 的值。参见迭代方法。

### 返回值

数组中第一个满足测试条件的元素的索引。否则返回 `-1`。

描述
--

`findIndex()` 是一种迭代方法。它按照索引升序依次遍历数组中的每个元素，并调用提供的 `callbackFn` 函数，直到 `callbackFn` 返回一个真值。然后 `findIndex()` 返回该元素的索引并停止遍历数组。如果 `callbackFn` 从未返回一个真值，则 `findIndex()` 返回 `-1`。

`callbackFn` 被调用来处理数组的*每一个*索引，而不仅仅是那些有值的索引。在稀疏数组中，未赋值的空槽与 `undefined` 表现相同。

`findIndex()` 不会改变被调用的数组，但是提供给 `callbackFn` 的函数可能会改变它。但需要注意的是，在第一次调用 `callbackFn` *之前*，数组的长度会被保存。因此：

* 当调用 `findIndex()` 时，`callbackFn` 不会访问超出数组初始长度的任何元素。
* 对已经访问过的索引的更改不会导致再次在这些元素上调用 `callbackFn`。
* 如果 `callbackFn` 改变了数组中已存在但尚未被访问的元素，则传递给 `callbackFn` 的该元素的值将是该元素在被访问时的值。被删除的元素被视为 `undefined`。

**警告：** 上述类型的并发修改经常导致难以理解的代码，通常应避免（特殊情况除外）。

`findIndex()` 方法是通用的。它只期望 `this` 值具有 `length` 属性和整数键属性。

示例
--

### 寻找数组中的首个素数的索引

以下示例返回数组中第一个素数的索引，如果没有素数则返回 `-1`。

```js
function isPrime(element) {
  if (element % 2 === 0 || element < 2) {
    return false;
  }
  for (let factor = 3; factor <= Math.sqrt(element); factor += 2) {
    if (element % factor === 0) {
      return false;
    }
  }
  return true;
}

console.log([4, 6, 8, 9, 12].findIndex(isPrime)); // -1，没有找到
console.log([4, 6, 7, 9, 12].findIndex(isPrime)); // 2（array[2] 是 7）
```

### 在稀疏数组上使用 findIndex()

你可以搜索稀疏数组中的 `undefined` 并来获取空槽的索引。

```js
console.log([1, , 3].findIndex((x) => x === undefined)); // 1
```

### 在非数组对象上调用 findIndex()

`findIndex()` 方法读取 `this` 的 `length` 属性，并访问每个整数索引。

```js
const arrayLike = {
  length: 3,
  0: 2,
  1: 7.3,
  2: 4,
};
console.log(
  Array.prototype.findIndex.call(arrayLike, (x) => !Number.isInteger(x)),
); // 1
```

规范
--

| Specification |
| --- |
| ECMAScript Language Specification # sec-array.prototype.findindex |

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

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

参见
--

* `core-js` 中 `Array.prototype.findIndex` 的 polyfill
* 索引集合类
* `Array`
* `Array.prototype.find()`
* `Array.prototype.findLast()`
* `Array.prototype.findLastIndex()`
* `Array.prototype.indexOf()`
* `Array.prototype.lastIndexOf()`
* `TypedArray.prototype.findIndex()`