前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
首页程序员面试题库flatMap 与 map filter 组合场景
JavaScript数组与集合

什么时候用 flatMap 比 map 加 filter 更合适?

flatMap 的回调返回 [] 就是删掉这一项,返回 [x] 就是替换,返回多个元素就是一变多。所以当过滤条件依赖映射后的值,或者一条数据要展开成多条时,用 flatMap 比 map 加 filter 更直接。只是简单地先筛再转,filter 加 map 读起来更清楚。

前端进阶之旅 · 一题精讲更新于 2026.09.29
JavaScript#数组与集合#性能优化
先看核心答案读代码示例
理解线索

回调返回值决定输出几项

  1. 零项返回 [],这一项从结果里消失,相当于 filter 删掉
  2. 一项返回 [x] 或者直接返回非数组值,相当于 map
  3. 多项返回 [a, b, c],这一项被展开成多项,map 和 filter 都做不到

flatMap 省掉了一个中间数组,但每次返回 []、[x] 也都是新数组。它是否更省内存、更快,要在真实数据上测,不能默认它一定快。

核心回答

先记住这个答案

flatMap 会把每一项的回调结果展开一层再拼起来,所以回调返回空数组就等于删除这一项,返回单元素数组等于替换,返回多个元素等于一项变多项。map 只能一对一,filter 只能决定留还是不留,两者都没法把一项变成多项。所以两类场景更适合 flatMap:一是过滤条件要依赖转换后的结果,比如解析失败的丢掉;二是一条记录要展开成多条,比如订单拆成商品行。至于性能,它少了一个中间数组,但每项都会新建小数组,是不是更快要实测。

  • 回调返回 [] 删除,[x] 保留,[a, b] 展开
  • 过滤要依赖转换结果时,用 flatMap 只算一次
  • 一条记录生成多行展示数据,天然适合 flatMap
  • 返回 null 不会被过滤掉,必须返回 []

过滤依赖转换结果时

从一组字符串里解析出合法的数字,解析失败的丢掉。用 map 加 filter 要先把所有项都转一遍,再筛一遍。

解析字符串,丢掉非法值JavaScript
const inputs = ['1', 'x', '3']

// map + filter:两次遍历,中间多一个数组
const a = inputs.map((s) => parseInt(s, 10)).filter((n) => !Number.isNaN(n))

// flatMap:一次遍历,不要的返回 []
const b = inputs.flatMap((s) => {
  const n = parseInt(s, 10)
  return Number.isNaN(n) ? [] : [n]
})

console.log(JSON.stringify(a), JSON.stringify(b))
查看输出与解释
[1,3] [1,3]

两种写法结果一样。map 加 filter 的问题在于,筛选用的是转换后的值,所以必须先转完。如果反过来先 filter 再 map,就得在两个回调里各解析一次。flatMap 把「算出来、判断要不要、放进结果」写在同一个回调里,逻辑集中。 在 TypeScript 里这个写法还有个好处:filter 想把 (number | null)[] 收窄成 number[],通常要写类型谓词;flatMap 返回 [] 或 [n],推断出来直接就是 number[]。

一条记录展开成多条

这是 flatMap 真正不可替代的地方。比如订单列表要渲染成「每个商品一行」。

订单拆成商品行JavaScript
const orders = [
  { id: 'A', items: ['笔', '本'] },
  { id: 'B', items: [] }, // 没有商品,直接消失
  { id: 'C', items: ['尺'] }
]

const rows = orders.flatMap((o) => o.items.map((name) => `${o.id}-${name}`))
console.log(JSON.stringify(rows))
查看输出与解释
["A-笔","A-本","C-尺"]

A 变成两行,B 因为 items 是空数组直接消失,C 是一行。用 map 会得到 [["A-笔","A-本"],[],["C-尺"]],还得再 flat 一次。类似的场景还有分词、把时间区间拆成每一天、树形菜单展开成扁平列表的一层。

什么时候反而不该用
如果只是「先过滤再转换」,而且过滤条件和转换无关,比如 users.filter((u) => u.active).map((u) => u.name),这时 filter 加 map 一眼就能看懂。改成 flatMap((u) => (u.active ? [u.name] : [])) 反而要多想一下。可读性优先,别为了少一次遍历牺牲清晰。
回答前,多想一步

容易答错的地方

用返回 null 或 undefined 来过滤
flatMap 只展开数组,返回非数组值会原样作为一项保留。[1, 2, 3].flatMap((x) => (x > 1 ? x : null)) 得到 [null, 2, 3],其中 null 还在。想删掉必须返回 []。
说 flatMap 一定比 map 加 filter 快
它少了一个完整的中间数组,但每次回调都新建了 [] 或 [n] 这样的小数组,分配次数不一定更少。实际快慢取决于数据规模和引擎优化。真到了性能热点,最省的是普通 for 循环直接 push 进结果数组。
试着用自己的话回答

面试官还会怎么问?

flatMap 能展开两层吗?

不能,深度固定为 1。回调返回 [[1, 2]],结果里是 [1, 2] 这一项,不会继续拆开。要多层展开,只能写 map(fn).flat(depth)。

用 reduce 也能同时过滤和映射,和 flatMap 比怎么样?

reduce 更通用,累加器可以是任何结构,但写法更啰嗦,要自己 push 或拼接再 return 累加器。结果就是一个数组时,flatMap 表达得更直接;需要同时产出计数、分组这类非数组结构时,才用 reduce。

回调里可以用 await 吗?

可以写,但 flatMap 不会等待。回调返回的是 Promise,不是数组,结果是一个 Promise 数组。异步场景要先用 Promise.all(arr.map(asyncFn)) 拿到结果,再对结果调用 flat 或 flatMap。

从一道题,走向一组知识

把知识连起来

数组与集合

Array.from 能转换类数组,为什么数组展开有时却会报错?

flatMap 只能用在数组上,数据是 Set 或 NodeList 时要先用 Array.from 转一下。

数组与集合

for...of 和 forEach 怎么跳过当前项、提前结束,异步时有什么区别?

需要中途退出或者逐项 await 时,flatMap 这类方法都不行,要换成 for...of。

数组与集合

flat 默认展开几层?flatMap 和先 map 再 flat 有什么不同?

flatMap 只展开一层,要更深的展开得回到 map 加 flat(depth)。

参考资料

  • MDN:flatMap

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. 过滤依赖转换结果时
  3. 一条记录展开成多条
  4. 容易答错的地方
  5. 面试官还会怎么问
  6. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑