Array.prototype.flatMap()
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]
语法
flatMap(callbackFn)
flatMap(callbackFn, thisArg)
参数
callbackFn-
为数组中的每个元素执行的函数。它应该返回一个包含新数组新元素的数组,或者一个要添加到新数组中的单个非数组值。该函数使用以下参数调用
thisArg可选-
在执行
callbackFn时用作this的值。请参阅 迭代方法。
返回值
一个新数组,其中每个元素都是回调函数的计算结果,并扁平化一级。
描述
flatMap() 方法是 迭代方法。有关回调函数的详细说明,请参阅 Array.prototype.map()。flatMap() 方法与 map(callbackFn, thisArg) 后跟 flat(1) 完全相同 — 对于每个元素,它会生成一个包含新元素的新数组,并将这些生成的数组连接起来形成一个新数组。有关这些方法如何工作的更多信息,请阅读 迭代方法 部分。
flatMap() 方法是 泛型。它只期望 this 值具有 length 属性和整数键属性。但是,从 callbackFn 返回的值如果是要扁平化的,则必须是数组。
替代方案
预先分配并显式迭代
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()
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() 用法的示例。
让我们从句子列表中生成一个单词列表。
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 个元素的数组以删除该项。
// 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() 创建一个新数组,其中每个元素包含一个站点及其下一个站点。对于最后一个站点,它返回一个空数组以将其从最终数组中排除。
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() 会忽略返回数组中的空槽。
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 属性,然后访问键为小于 length 的非负整数的每个属性。如果回调函数的返回值不是数组,它总是直接附加到结果数组中。
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 |
浏览器兼容性
加载中…