Array.prototype.flatMap()

Baseline 已广泛支持

此特性已相当成熟,可在许多设备和浏览器版本上使用。自 ⁨2020 年 1 月⁩ 起,所有主流浏览器均已支持。

flatMap() 方法 Array 实例返回一个新数组,该数组是通过将给定的回调函数应用于数组的每个元素,然后将结果扁平化一级而形成的。它与 map() 后跟一个深度为 1 的 flat()arr.map(...args).flat())完全相同,但比单独调用这两个方法效率稍高。

试一试

const arr = [1, 2, 1];

const result = arr.flatMap((num) => (num === 2 ? [2, 2] : 1));

console.log(result);
// Expected output: Array [1, 2, 2, 1]

语法

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]);
// is equivalent to
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 arr = [1, 2, 3, 4];

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

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

// only one level is flattened
arr.flatMap((x) => [[x * 2]]);
// [[2], [4], [6], [8]]

虽然上面的例子可以用 map 来实现,但这里有一个更好地展示 flatMap() 用法的示例。

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

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

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

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

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

在 map() 中添加和删除项目

flatMap 可用作在 map 过程中添加和删除项目(修改项目数量)的方法。换句话说,它允许你将多个项目映射到多个项目(通过单独处理每个输入项),而不是始终一对一。从这个意义上说,它的作用与 filter 相反。返回一个包含 1 个元素的数组以保留该项,返回一个包含多个元素的数组以添加项,或者返回一个包含 0 个元素的数组以删除该项。

js
// Let's say we want to remove all the negative numbers
// and split the odd numbers into an even number and a 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]

使用 callbackFn 的第三个参数

如果想访问数组中的另一个元素,array 参数就很有用,尤其是在没有引用该数组的现有变量时。以下示例首先使用 filter() 提取操作站,然后使用 flatMap() 创建一个新数组,其中每个元素包含一个站点及其下一个站点。对于最后一个站点,它返回一个空数组以将其从最终数组中排除。

js
const stations = ["New Haven", "West Haven", "Milford (closed)", "Stratford"];
const line = stations
  .filter((name) => !name.endsWith("(closed)"))
  .flatMap((name, idx, arr) => {
    // Without the arr argument, there's no way to easily access the
    // intermediate array without saving it to a variable.
    if (idx === arr.length - 1) return []; // last station has no next station
    return [`${name} - ${arr[idx + 1]}`];
  });
console.log(line); // ['New Haven - West Haven', 'West Haven - Stratford']

array 参数不是正在构建的数组——无法从回调函数访问正在构建的数组。

在稀疏数组上使用 flatMap()

由于 map() 不会调用回调函数,因此 callbackFn 不会被调用来处理源数组中的空槽,而 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() 方法读取 thislength 属性,然后访问键为小于 length 的非负整数的每个属性。如果回调函数的返回值不是数组,它总是直接附加到结果数组中。

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

// Array-like objects returned from the callback won't be flattened
console.log(
  Array.prototype.flatMap.call(arrayLike, (x) => ({
    length: 1,
    0: x,
  })),
);
// [ { '0': 1, length: 1 }, { '0': 2, length: 1 }, { '0': 3, length: 1 } ]

规范

规范
ECMAScript® 2026 语言规范
# sec-array.prototype.flatmap

浏览器兼容性

另见