🔥 面试必考题:手写数组扁平化,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 种写法,不仅能轻松应对面试,更能提升你对 递归、函数式编程、数据结构 的理解。

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

相关推荐
Stringzhua2 分钟前
ElementUi【饿了么ui】
前端·ui·elementui
怪兽20144 分钟前
主线程 MainLooper 和一般 Looper 的异同?
android·面试
.NET修仙日记9 分钟前
SQL Server实战指南:从基础CRUD到高并发处理的完整面试题库
面试·职场和发展·c#·.net·sql server·.net全栈经典面试题库
古一|18 分钟前
vue3都有哪些升级相比vue2-核心响应式系统重构
javascript·vue.js·重构
HHHHHY19 分钟前
http接口响应头类型不对,导致svg图片无法预览,前端解决方案
前端·javascript
Komorebi゛28 分钟前
【React】配置别名路径
前端·react.js·前端框架
风语者日志31 分钟前
CTFSHOW WEB 3
前端
元亓亓亓1 小时前
考研408--组成原理--day1
开发语言·javascript·考研·计组
普通码农1 小时前
uni.setClipboardData在 iOS 剪贴板复制失败解决方案
前端
_孤傲_1 小时前
webpack实现常用plugin
前端·webpack·node.js