用 reduce 实现数组拍平的核心是:遍历每一项,若为数组则递归展开,否则保留原值,再用 concat 合并;基础单层拍平只处理一层嵌套,无限深度拍平则递归到底,concat 天然兼容单个值和数组。

用 reduce 实现数组拍平(flat),核心是:遍历每一项,若为数组则递归展开,否则保留原值,再用 concat 合并。
基础单层拍平(flat(1))
只处理一层嵌套,不递归:
const flatOnce = arr => arr.reduce((acc, item) => acc.concat(item), []);
说明:
- 初始值设为空数组 [];
- 遇到数组(如 [1, 2])直接 concat 进去,自动展开;
- 遇到非数组(如 3)concat(3) 等价于 [...acc, 3],安全无副作用。
例如:flatOnce([1, [2, 3], 4, [5]]) → [1, 2, 3, 4, 5]
支持指定深度的递归拍平
传入深度 depth,为 0 时停止递归:
const flat = (arr, depth = 1) => {
if (depth === 0) return arr;
return arr.reduce((acc, item) => {
return acc.concat(Array.isArray(item) ? flat(item, depth - 1) : item);
}, []);
};
关键点:
- 每次递归调用前将 depth - 1;
- 仅当 item 是数组才继续递归,否则直接拼入;
- concat 天然兼容单个值和数组,无需额外判断。
例如:flat([1, [2, [3, [4]]]], 2) → [1, 2, 3, [4]]
无限深度拍平(flat(Infinity))
把 depth 设为一个极大数,或改用类型判断终止:
const flatDeep = arr => {
return arr.reduce((acc, item) => {
return acc.concat(Array.isArray(item) ? flatDeep(item) : item);
}, []);
};
注意:
- 无深度限制,所有嵌套数组都会被彻底展开;
- 若数据含循环引用,会爆栈,生产环境需加检测;
- 对比原生 arr.flat(Infinity),行为一致。
例如:flatDeep([1, [2, [3]], [[4, 5], 6]]) → [1, 2, 3, 4, 5, 6]
小技巧:避免 concat 的隐式转换问题
concat 对类数组或有 Symbol.isConcatSpreadable 的对象也会展开,若需严格只处理真数组,可加强判断:
const safeFlat = (arr, depth = 1) => {
if (depth === 0) return arr;
return arr.reduce((acc, item) => {
const toPush = Array.isArray(item) && depth > 0
? safeFlat(item, depth - 1)
: item;
return acc.concat(toPush);
}, []);
};
这样能防止意外展开 arguments、NodeList 等类数组对象。











