🔥 面试必考题:手写数组扁平化,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);
}

✅ 优点:

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

⚠️ 缺点:

  • 只适用于数字数组,字符串会出错
  • null、undefined、对象等会变成 "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 种写法,不仅能轻松应对面试,更能提升你对 递归、函数式编程、数据结构 的理解。

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

相关推荐
程序员Sunday3 小时前
JavaScript 事件循环面试题,宏任务与微任务怎么执行|Sunday面试指南
开发语言·javascript·面试·校招·事件循环·程序员sunday
Setsuna_F_Seiei3 小时前
前端转型 Agent 开发 06 之 Agent Memory 记忆系统(让 Agent 更智能,更懂你)
前端·agent·ai编程
YangYang9YangYan3 小时前
2027 秋招|数据科学与大数据技术面试项目价值回答思路,告别纯代码描述
大数据·面试·职场和发展
zhangzeyuaaa4 小时前
Ruby 多线程、GVL(GIL)与 Mutex 完全指南
开发语言·前端·ruby
默_笙4 小时前
🌊 向量库和 ES 都查不出"关系",我只好给奶茶建了一张人脉网
前端·javascript
邓江洲4 小时前
一道经典的 NSString 内存面试题
面试
优选资讯5 小时前
标签打印软件怎么对接 Excel 数据
前端·excel
思无邪665 小时前
用 AI 做 JS 逆向:从抓包到复现的完整方法论
开发语言·javascript·人工智能
一个风轻云淡5 小时前
GCC 和 GDB命令简单解读
java·linux·前端
u0111026756 小时前
图片处理接口如何统一错误信息 前端提示与后端错误码设计
前端·状态模式