跳到主要内容

flat / flatMap:数组扁平化怎么做?和 map+flat 有什么区别?

面试速答(30 秒版 TL;DR)

  • Array.prototype.flat(depth = 1) 会把数组按深度展开,常用 flat(Infinity) 做彻底扁平化。
  • flatMap(fn) 等价于 map(fn).flat(1),但一次遍历更高效且语义更清晰。
  • flat 会跳过空洞(holes),这点在面试里容易被追问。
  • 老环境可用递归或 reduce 手写扁平化,但要注意性能与栈深度风险。

最小例子:flat 的深度

console.log([1, [2, [3]]].flat()); // [1, 2, [3]]
console.log([1, [2, [3]]].flat(2)); // [1, 2, 3]
console.log([1, [2, [3]]].flat(Infinity)); // [1, 2, 3]

空洞(holes):flat 会怎么处理

const a = [1, , 3]; // 中间是空洞
console.log(a.length); // 3
console.log(a.flat()); // [1, 3]

口述要点:

  • flat 会移除空洞,这和一些遍历方法对空洞的处理方式不同,写题时要注意。

flatMap:为什么它不是“随便的语法糖”

const words = ["a b", "c d"];
const out1 = words.map((s) => s.split(" ")).flat();
const out2 = words.flatMap((s) => s.split(" "));
console.log(out1); // ["a","b","c","d"]
console.log(out2); // ["a","b","c","d"]

面试回答重点:

  • flatMap 把“映射 + 展开一层”变成一个 API,语义更直接,也可能更省一次中间数组。

手写扁平化(面试可写)

版本 1:递归(直观,但注意深度)

function flatten(arr, depth = 1) {
const out = [];
for (const item of arr) {
if (Array.isArray(item) && depth > 0) {
out.push(...flatten(item, depth === Infinity ? Infinity : depth - 1));
} else {
out.push(item);
}
}
return out;
}

版本 2:迭代(更稳,避免递归栈)

function flattenInfinity(arr) {
const stack = [...arr];
const out = [];
while (stack.length) {
const v = stack.pop();
if (Array.isArray(v)) {
for (let i = v.length - 1; i >= 0; i--) stack.push(v[i]);
} else {
out.push(v);
}
}
out.reverse();
return out;
}

典型题 & 标准答法

Q1:flatconcat 的区别?

  • concat 只会把参数数组展开一层(且不是递归展开嵌套)
  • flat 是明确的“按深度展开嵌套数组”

Q2:什么时候用 flatMap

当你每个元素映射后会得到“数组”,并且只需要展开一层时用 flatMap 最合适。


易错点/坑

  • flatMap 只能展开一层,想多层需要 flat 或自己递归。
  • 对超深嵌套的数组,递归实现有调用栈溢出的风险。

速记要点(可背诵)

  • flat(depth),彻底展开用 Infinity
  • flatMap = map + flat(1)
  • flat 会移除空洞。