flatMap能同时过滤和拍平,因其本质是map+flat(1),返回空数组[]可跳过元素,返回非数组值会被自动包装,故需统一返回数组。

flatMap 为什么能同时过滤和拍平
flatMap 本质是 map + flat(1) 的组合,但关键在于:它对 map 返回的每个子数组只展开一层,且允许你返回空数组([])来实现“过滤”效果。这不是语法糖的巧合,而是设计使然——返回空数组会被展开成零个元素,自然跳过该条目。
常见错误是试图在 flatMap 回调里写 if (...) return item,却忘了必须统一返回数组。只要返回非数组值(比如对象或字符串),flatMap 会自动包装成单元素数组,导致本想过滤的项反而保留了。
- ✅ 正确:满足条件时返回
[item],不满足时返回[] - ❌ 错误:条件成立返回
item(会被转成[item]),条件不成立返回undefined(被转成[undefined]) - ⚠️ 注意:
flatMap不会递归展开嵌套数组,只做一层flat(1),所以[[1, [2, 3]]]经flatMap(x => x)后是[1, [2, 3]],不是[1, 2, 3]
处理含 null/undefined 元素的嵌套数组
实际数据常含脏值,比如后端返回的 items: (Item | null)[][],既要剔除 null,又要拍平两层结构。直接用 flat(2) 再 filter(Boolean) 是两步,而 flatMap 可合并:
const data = [[{id: 1}, null], [null, {id: 2}], [{id: 3}]];
const result = data.flatMap(arr => arr.filter(Boolean));
// → [{id: 1}, {id: 2}, {id: 3}]
这里利用了 flatMap 的第一层映射作用:把原数组每个子项 arr 当作一个待处理单元;再用 filter(Boolean) 清洗掉 null,最后由 flatMap 自动拍平结果。比先 flat(1) 再 filter 少一次遍历,也避免中间生成全量扁平数组。
- 若子数组本身还含数组(如
[[1, [2, 3]], [4]]),flatMap无法替代flat(2),需改用flat().filter() - IE 完全不支持
flatMap,需搭配core-js或降级为reduce((acc, cur) => acc.concat(cur.filter(Boolean)), [])
替代 map + flat(1) 时的性能与可读权衡
当逻辑简单(如仅拍平+去空),map(x => x).flat(1) 和 flatMap(x => x) 行为一致,但后者少建一次中间数组。V8 引擎对 flatMap 有专门优化,实测大数据量下快 10%–20%。
但一旦加入条件分支,可读性就分水岭了:
// 可读性差:嵌套三元、混用 filter/map
list.flatMap(x => x.type === 'user' ? [x.profile] : x.items?.length ? x.items : []);
// 更清晰:拆成 if-return 数组
list.flatMap(x => {
if (x.type === 'user') return [x.profile];
if (Array.isArray(x.items)) return x.items;
return [];
});
- 不要为了“一步到位”硬塞复杂逻辑进单行箭头函数
-
flatMap的回调函数体建议控制在 5 行内,超出行数不如拆成独立函数 - TypeScript 用户注意:返回类型必须是
T[],不能是T | T[],否则推导失败
与 reduce 实现等价逻辑的边界对比
有人习惯用 reduce 模拟 flatMap: arr.reduce((acc, x) => acc.concat(x), [])。这看似灵活,但有两个隐藏成本:
- 每次
concat都新建数组,时间复杂度 O(n²),而flatMap是 O(n) - 无法天然跳过空项——
concat(undefined)会插入undefined,还得额外filter - 真正需要
reduce的场景是:拍平过程中要累积状态(如计数、去重 Map),而非单纯转换
例如,统计每组拍平后的元素来源索引:flatMap((item, i) => item.map(x => ({...x, srcIndex: i}))) 就比 reduce 直观得多,且无性能损失。
最易被忽略的是:flatMap 对稀疏数组保持稀疏性(如 [1, , 3] 的 flatMap(x => [x]) 仍是稀疏),而 reduce + concat 会抹平稀疏性,变成稠密数组。如果业务依赖 in 操作符或 for...in 遍历,这点必须验证。

















