🔥 面试必考题:手写数组扁平化,5种方法全解析(附代码+图解)

2025年9月25日 · 前端进阶 · 手撕代码系列

在前端开发中,数组扁平化 是一个高频面试题,几乎每一场大厂面试都会被问到:"如何将一个多维数组变成一维数组?"

今天我们就来彻底搞懂这个问题,从递归、reduce、栈模拟到 ES6 新特性,5种主流实现方式一次性讲透,助你轻松应对面试官的灵魂拷问!


🧠 什么是数组扁平化?

数组扁平化(Flatten) 就是把一个嵌套多层的数组转换为只有一层的一维数组。

例如:

js 复制代码
[1, [2, [3, 4]], 5] → [1, 2, 3, 4, 5]

看似简单,但背后考察的是你对 递归、高阶函数、数据结构 的理解深度。


✅ 方法一:递归 + concat(最经典)

这是最直观、最容易理解的方法,也是面试官最希望听到的"基础解法"。

js 复制代码
function flatten(arr) {
    let res = [];
    for (let item of arr) {
        if (Array.isArray(item)) {
            // 如果是数组,递归展开并 concat 到结果中
            res = res.concat(flatten(item));
        } else {
            // 否则直接 push
            res.push(item);
        }
    }
    return res;
}

✅ 核心逻辑:

  • 递归入口:遇到数组就继续 flatten
  • 递归出口:遇到非数组项,直接返回
  • 拼接方式 :使用 concat 合并子数组

⚠️ 缺点:

  • concat 每次都会创建新数组,性能较差(尤其深层嵌套时)
  • 递归调用栈可能溢出(极端情况)

✅ 方法二:reduce + 递归(函数式编程风)

利用 reduce 累加器思想,更符合函数式编程风格,代码更简洁。

js 复制代码
const flatten = arr =>
    arr.reduce((acc, cur) =>
        acc.concat(Array.isArray(cur) ? flatten(cur) : cur), []);

✅ 优点:

  • 一行代码搞定,优雅简洁
  • 函数式思维清晰

⚠️ 缺点:

  • 依然存在 concat 性能问题
  • 可读性略低于 for 循环版本

✅ 方法三:栈模拟(避免递归爆栈)

栈(Stack) 模拟递归过程,避免深层递归导致的调用栈溢出。

js 复制代码
function flatten(arr) {
    const stack = [...arr];  // 模拟调用栈
    const res = [];

    while (stack.length) {
        const item = stack.pop();  // LIFO:后进先出

        if (Array.isArray(item)) {
            // 是数组,将其元素重新压入栈
            stack.push(...item);
        } else {
            // 非数组,直接加入结果
            res.push(item);
        }
    }

    return res.reverse();  // 因为是倒序出栈,所以需要反转
}

✅ 优点:

  • 避免递归爆栈,适合处理超深嵌套
  • 时间复杂度稳定,性能优于递归
  • 体现对数据结构的理解

💡 图解栈过程:

js 复制代码
初始栈: [1, [2, [3, 4]], 5]
→ pop 5 → push 5 → res=[5]
→ pop [2, [3,4]] → push 2, [3,4] → 栈变为 [1, 2, [3,4]]
→ pop [3,4] → push 3,4 → 栈变为 [1,2,3,4]
→ pop 4,3,2,1 → res=[5,4,3,2,1] → reverse → [1,2,3,4,5]

✅ 方法四:ES6 flat() API(现代写法)

ES2019 引入了原生 flat() 方法,一行代码解决战斗!

js 复制代码
console.log([1, [2, [3, 4]], 5].flat(Infinity));
// 输出: [1, 2, 3, 4, 5]

✅ 优点:

  • 语法极简,语义清晰
  • 性能优秀,由引擎优化
  • 支持指定扁平化深度:flat(1)flat(2)

⚠️ 注意:

  • flat(Infinity) 可以完全扁平化任意层数
  • 需注意浏览器兼容性(IE 不支持)

✅ 方法五:toString + split(取巧但有限制)

利用数组 toString 会自动扁平化的特性:

js 复制代码
function flatten(arr) {
    return arr.toString().split(',').map(Number);
}

✅ 优点:

  • 极简!一行搞定
  • 适合纯数字数组

⚠️ 缺点:

  • 只适用于数字数组,字符串会出错
  • nullundefined、对象等会变成 "null" 字符串
  • 类型转换不安全

❌ 不推荐用于生产环境,仅作"骚操作"了解。


📊 五种方法对比总结

方法 是否递归 是否安全 性能 推荐指数
递归 + concat ⭐⭐ ⭐⭐⭐
reduce + 递归 ⭐⭐ ⭐⭐⭐⭐
栈模拟 ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
flat(Infinity) ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
toString + split ⭐⭐⭐

💡 面试加分技巧

  1. 主动说明边界情况

    js 复制代码
    flatten([1, [2, [3, [4, [5]]]], 6]) // 深层嵌套
    flatten([1, null, undefined, [2]])   // null/undefined
    flatten([])                          // 空数组
  2. 扩展:支持 depth 参数

    js 复制代码
    function flatten(arr, depth = Infinity) {
        if (depth === 0) return arr;
        let res = [];
        for (let item of arr) {
            if (Array.isArray(item)) {
                res.push(...flatten(item, depth - 1));
            } else {
                res.push(item);
            }
        }
        return res;
    }
  3. 说出 flat() 的 polyfill 思路

    "现代项目我推荐用 flat(),但如果要兼容旧环境,我会用栈模拟实现一个 polyfill。"


✅ 总结:面试如何回答?

面试官:请手写一个数组扁平化函数

✅ 标准回答结构:

  1. 定义问题:先说明什么是扁平化
  2. 给出基础解法:递归 + isArray
  3. 优化方案:提到 reduce 和栈模拟
  4. 现代方案:flat() 是首选
  5. 扩展思考:兼容性、性能、边界

"我会优先使用 Array.prototype.flat(Infinity),因为它简洁高效。如果需要兼容老环境,我会用栈模拟避免递归爆栈,同时保证性能稳定。"


📣 结语

数组扁平化虽小,却能全面考察你的编程思维。掌握这 5 种写法,不仅能轻松应对面试,更能提升你对 递归、函数式编程、数据结构 的理解。

🔖 收藏 + 转发,下次面试前翻出来看一眼,稳了!

相关推荐
jingling5551 分钟前
vue | 在 Vue 3 项目中集成高德地图(AMap)
前端·javascript·vue.js
油丶酸萝卜别吃1 分钟前
Vue3 中如何在 setup 语法糖下,通过 Layer 弹窗组件弹出自定义 Vue 组件?
前端·vue.js·arcgis
J***Q2927 小时前
Vue数据可视化
前端·vue.js·信息可视化
ttod_qzstudio8 小时前
深入理解 Vue 3 的 h 函数:构建动态 UI 的利器
前端·vue.js
芳草萋萋鹦鹉洲哦8 小时前
【elemen/js】阻塞UI线程导致的开关卡顿如何优化
开发语言·javascript·ui
_大龄9 小时前
前端解析excel
前端·excel
1***s6329 小时前
Vue图像处理开发
javascript·vue.js·ecmascript
槁***耿9 小时前
JavaScript在Node.js中的事件发射器
开发语言·javascript·node.js
一叶茶9 小时前
移动端平板打开的三种模式。
前端·javascript
前端大卫9 小时前
一文搞懂 Webpack 分包:async、initial 与 all 的区别【附源码】
前端