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:flat 和 concat 的区别?
concat只会把参数数组展开一层(且不是递归展开嵌套)flat是明确的“按深度展开嵌套数组”
Q2:什么时候用 flatMap?
当你每个元素映射后会得到“数组”,并且只需要展开一层时用 flatMap 最合适。
易错点/坑
flatMap只能展开一层,想多层需要flat或自己递归。- 对超深嵌套的数组,递归实现有调用栈溢出的风险。
速记要点(可背诵)
flat(depth),彻底展开用Infinity。flatMap = map + flat(1)。flat会移除空洞。