大家好,我是江城开朗的豌豆,一名拥有6年以上前端开发经验的工程师。我精通HTML、CSS、JavaScript
等基础前端技术,并深入掌握Vue、React、Uniapp、Flutter
等主流框架,能够高效解决各类前端开发问题。在我的技术栈中,除了常见的前端开发技术,我还擅长3D开发,熟练使用Three.js
进行3D图形绘制,并在虚拟现实与数字孪生技术上积累了丰富的经验,特别是在虚幻引擎开发方面,有着深入的理解和实践。
我一直认为技术的不断探索和实践是进步的源泉,近年来,我深入研究大数据算法的应用与发展,尤其在数据可视化和交互体验方面,取得了显著的成果。我也注重与团队的合作,能够有效地推动项目的进展和优化开发流程。现在,我担任全栈工程师,拥有CSDN博客专家认证及阿里云专家博主称号,希望通过分享我的技术心得与经验,帮助更多人提升自己的技术水平,成为更优秀的开发者。
技术qq交流群:906392632
大家好,我是小杨,做了6年前端。今天要聊一个看似简单却让无数新手抓狂的话题------数组扁平化 。还记得我第一次面试时被问到"如何拍平多维数组",当场表演了"用递归递归递归..."的卡带现场,现在想起来脚趾还能抠出三室一厅 😅。今天就把这些年的经验总结成六大必杀技,保你看完直呼:"原来还能这么玩!"
一、生活场景:什么叫"数组不扁平"?
想象你收拾衣柜:
- 千层饼数组 :
[袜子, [裤子, [上衣, 腰带]], 鞋子]
(找双袜子要翻三层) - 扁平数组 :
[袜子, 裤子,上衣,腰带,鞋子]
(一目了然)
需求:把嵌套数组变成一维数组,就是扁平化!
二、青铜选手:flat()
方法(ES10真香)
javascript
const messyCloset = ['袜子', ['裤子', ['上衣', '腰带']], '鞋子'];
// 1. 默认只拍平一层
const level1 = messyCloset.flat();
// ['袜子', '裤子', ['上衣', '腰带'], '鞋子']
// 2. 传入Infinity直接全平
const allFlat = messyCloset.flat(Infinity);
// ['袜子', '裤子', '上衣', '腰带', '鞋子']
我的翻车史 :
曾经以为flat(2)
和flat(Infinity)
效果一样,直到遇到五层嵌套数组...
三、白银选手:reduce
+递归(经典永流传)
javascript
function flatten(arr) {
return arr.reduce((result, item) => {
return result.concat(
Array.isArray(item) ? flatten(item) : item
);
}, []);
}
const myCloset = ['👖', ['👔', ['🧦']], '👟'];
console.log(flatten(myCloset)); // ['👖', '👔', '🧦', '👟']
适用场景:
- 需要兼容老浏览器时
- 面试官盯着你手写实现时 😏
四、黄金选手:toString
妙用(数字数组专属)
javascript
const numLayers = [1, [2, [3, [4]]]];
// 1. 转字符串再分割
const strFlat = numLayers.toString().split(',');
// ['1', '2', '3', '4']
// 2. 转数字(需处理非数字项)
const numFlat = strFlat.map(Number);
// [1, 2, 3, 4]
致命缺陷:
- 会把
[1, 'a']
变成['1', 'a']
- 遇到
null, undefined
直接转字符串
五、铂金选手:生成器函数(优雅处理海量数据)
javascript
function* flattenGen(arr) {
for (const item of arr) {
Array.isArray(item) ? yield* flattenGen(item) : yield item;
}
}
const hugeArray = [1, [2, [3, [4, ...[1000000个项]]]];
const flattened = [...flattenGen(hugeArray)]; // 按需生成不爆内存
性能优势 :
处理超大规模数组时,不会一次性占用大量内存
六、钻石选手:栈结构迭代(避免递归爆栈)
javascript
function flattenStack(arr) {
const stack = [...arr];
const result = [];
while (stack.length) {
const next = stack.pop();
Array.isArray(next) ? stack.push(...next) : result.push(next);
}
return result.reverse();
}
const deepArray = [1, [2, [3, [4]]];
console.log(flattenStack(deepArray)); // [1, 2, 3, 4]
适用场景:
- 嵌套层级极深时(递归可能栈溢出)
- 需要控制遍历顺序时
七、王者选手:Array.prototype.flatMap
(ES2019)
javascript
// 先map再flat(1)的语法糖
const data = [1, [2], 3];
const processed = data.flatMap(item =>
Array.isArray(item) ? item : [item * 2]
);
// [2, 2, 6]
神技巧 :
用flatMap
实现过滤+展开二合一:
javascript
const mixed = [1, 2, , , 5];
const compact = mixed.flatMap(x => x || []);
// [1, 2, 5] (自动跳过空值)
八、终极选择指南
场景 | 推荐方案 | 原因 |
---|---|---|
现代浏览器环境 | flat(Infinity) |
原生API性能最佳 |
需要兼容IE | reduce +递归 |
兼容性好 |
纯数字数组 | toString +split |
取巧但高效 |
超大数据量 | 生成器函数 | 内存友好 |
需要控制遍历顺序 | 栈结构迭代 | 避免递归爆栈 |
需要边处理边展开 | flatMap |
代码最简洁 |
九、真实案例:树形菜单扁平化
javascript
// 原始树形数据
const menuTree = [
{
name: '文件',
children: [
{ name: '新建' },
{ name: '打开', children: [{ name: '从云端' }] }
]
}
];
// 递归提取所有节点名
function getAllNames(items) {
return items.flatMap(item => [
item.name,
...(item.children ? getAllNames(item.children) : [])
]);
}
console.log(getAllNames(menuTree));
// ['文件', '新建', '打开', '从云端']
十、总结:一句话记住各方案
flat()
:官方指定快乐锤 🔨reduce
:面试必备手写题 ✍️toString
:数字数组急救包 🚑- 生成器:海量数据救世主 🦸
- 栈迭代:深度嵌套终结者 💣
flatMap
:过滤展开二合一 🥇
最后灵魂提问:你们团队用什么方案?有没有更骚的操作?评论区见真章! 👇