1. ES6 数组 flatMap 方法概览
1.1 定义与工作原理
在现代前端开发中,flatMap 是对数组最常用的变换之一。它在一个方法调用中同时执行 映射 和 扁平化,本质是对数组成员执行回调获取结果数组,然后将结果的多维结构减少为一维,扁平化深度为 1。
与传统的 map 不同,flatMap 的返回值是一个一维数组,哪怕回调返回的是嵌套数组。它避免了中间临时数组的拼接,简化了代码,提升可读性。
const source = [1, 2, [3, 4]];
const mapped = source.flatMap(x => x); // [1, 2, 3, 4]
1.2 为什么要使用 flatMap
在处理 API 返回的数据或复杂数据结构时,扁平化和映射同时发生会让代码更简洁。使用 flatMap 可以避免额外的二次遍历和手动拼接,减少潜在的错误。
另外,单层扁平化 的需求很常见。如果需要多层扁平化,应该组合使用 flatMap 与 flat,或者采用递归解决方案。
2. flatMap 的用法要点
2.1 基本用法与语法要点
最常见的写法是:array.flatMap(callback),回调对每个元素返回一个数组,最终结果是将所有这些数组在第一层拼接起来的结果。注意:callback 可以返回任意数组,但返回的不是数组时会影响结果或报错。
一个简单示例展示了基本行为:原始数组被映射并扁平化为一个一维数组,避免了手动 concat 的繁琐。
const nums = [1, 2, 3];
const res = nums.flatMap(n => [n, n * 10]); // [1, 10, 2, 20, 3, 30]
2.2 回调返回值的形式与边界情况
为实现正确的扁平化,回调必须返回一个数组(或类数组),否则结果可能不同步。对于单值返回,可以用 [value] 的形式来包裹,确保扁平化的一致性。
在处理空值或 undefined 时,要注意:返回空数组 或使用条件判断,避免引入额外的 undefined 元素。
const mixed = [1, null, [2, 3], undefined];
const safe = mixed.flatMap(x => (Array.isArray(x) ? x : [x]).filter(v => v != null)); // [1, 2, 3]
3. flatMap 与 map 的区别
3.1 返回结构与扁平化深度的差异
核心区别在于返回结构:map 仅改变每个元素的值,不进行扁平化;flatMap 则在映射后进行一次扁平化,所以最终结构往往是一维或更低层级的嵌套更少的数组。
要点总结:map() 的回调返回值自行决定输出结构,而 flatMap() 的回调应返回一个数组以实现第一层级的扁平化。
const data = [1, 2, 3];
// map 示例
const mapped = data.map(n => [n, n * 2]); // [[1,2], [2,4], [3,6]]
// flatMap 示例
const flat = data.flatMap(n => [n, n * 2]); // [1, 2, 2, 4, 3, 6]
3.2 性能与可读性比较
在可读性方面,flatMap 将映射与扁平化合二为一,通常比先 map 再使用 flat 更直观,代码量也更少。
从性能角度,避免了中间数组的创建,可能略微提升效率,但这也取决于回调的复杂性和数据规模。对于极大数据集合,仍需进行基准测试以确定最优实现。

// map + flat 的对比
const arr = [1, 2, [3, 4]];
const mapThenFlat = arr.map(x => x).flat(); // [1, 2, 3, 4]// flatMap 的实现逻辑接近 map + flatten1
const fm = arr.flatMap(x => x); // [1, 2, 3, 4]
4. 实战场景:使用 flatMap 解决实际问题
4.1 处理嵌套数组并提取可用值
当你从服务器获取的数据包含嵌套数组字段时,flatMap 可以直接将嵌套数组“拉平”并在同一步骤中进行提取与映射,减少数据处理链的复杂度。
示例场景:将一个对象数组中的标签数组提取成一个扁平化的标签列表,同时对标签做过滤。
const products = [{ id: 1, tags: ['New', 'Sale'] },{ id: 2, tags: ['Popular'] },{ id: 3, tags: [] }
];
const tags = products.flatMap(p => p.tags).filter(t => t !== 'Sale'); // ['New','Popular']
4.2 与数据映射结合的实战
在将服务器返回的对象数组映射为前端展示结构时,flatMap 可以直接展开子字段并创建新的展示项。使用回调创建需要的对象结构,减少中间变量。
举例:将用户数据映射为 [{ id, name, roles }] 的格式,并将多级角色信息扁平化呈现。
const users = [{ id: 1, name: 'Alice', roles: ['admin', 'editor'] },{ id: 2, name: 'Bob', roles: ['viewer'] }
];
const view = users.flatMap(u => [{ id: u.id, name: u.name, role: r } for (r of u.roles)]);
// 等价于展平后的对象集合
4.3 与 DOM 操作或索引化结合的技巧
在生成 UI 列表时,flatMap 可以与生成元素的映射逻辑结合,直接得到一个扁平结构的结果,方便后续的渲染或索引。避免额外的中间步骤,使渲染逻辑更清晰。
下例展示了从嵌套数据创建一个扁平的文字列表:
const sections = [{ title: 'A', items: ['a1', 'a2'] },{ title: 'B', items: ['b1'] }
];
const itemsList = sections.flatMap(s => s.items.map(it => `${s.title}: ${it}`));
// ['A: a1', 'A: a2', 'B: b1']
5. 兼容性与降级方案
5.1 浏览器兼容性与生态环境
flatMap 作为 ES2019(ES10) 的标准方法,在现代浏览器中已经得到广泛支持。Chrome、Edge、Firefox、Safari 的最新版本都原生支持它。
如果需要兼容较旧的环境,可以通过 polyfill 实现等效功能,或使用 map + flat(1) 的组合来替代。
// 简单 polyfill(仅示例,正式项目应使用 core-js 等库)
if (!Array.prototype.flatMap) {Array.prototype.flatMap = function(fn) {return this.reduce((acc, x, i, arr) => acc.concat(fn(x, i, arr)), []);};
}
5.2 替代方案与降级策略
在不可用 flatMap 的环境中,可以使用 reduce 实现等价的映射+扁平化逻辑,但可读性较低,维护成本较高。若需要保持兼容,可以回退。
一个常见的降级模式是:先 map 生成子数组,再在外部使用 flat 或自定义的拼接逻辑实现扁平化。这样可以保持与老版本实现的兼容性。
const data = [1, 2, 3];
const safely = data.map(n => [n, n * 2]).flat(); // [1, 2, 2, 4, 3, 6]


