JavaScript 数组扁平化:四种方法详解与最佳实践

简介: 本文详解JavaScript数组扁平化的四种主流方法:`flat()`、扩展运算符+`concat`、`reduce`和`for...of`循环,从语法、性能、兼容性等维度对比分析,结合适用场景与最佳实践,助你高效处理嵌套数组。

@TOC

JavaScript 数组扁平化:四种方法详解与最佳实践

在日常开发中,我们经常会遇到嵌套数组(nested arrays)的处理需求。例如,从 API 接口返回的数据可能包含多层结构,而我们需要将其“拍平”成一维数组以便后续操作。本文将以如下示例数据为切入点,系统性地介绍四种主流的数组扁平化方法,并从语法简洁性、执行性能、兼容性、适用场景等多个维度进行对比分析。

const arr = [[1, 2], [3, 4], [5, 6]];
// 目标:[1, 2, 3, 4, 5, 6]

方法 1:Array.prototype.flat()(现代标准,推荐)

基本用法

ES2019(ECMAScript 2019)引入了 flat() 方法,专门用于数组扁平化,语法简洁直观:

const merged = arr.flat();
console.log(merged); // [1, 2, 3, 4, 5, 6]

深度控制

  • 默认只展开一层嵌套。
  • 可通过传入参数指定展开深度:
    const deepArr = [[[1, 2]], [[3, 4]]];
    console.log(deepArr.flat(2));        // [[1, 2], [3, 4]]
    console.log(deepArr.flat(Infinity)); // [1, 2, 3, 4] — 完全扁平化
    

优势

  • 语义清晰:专为扁平化设计,意图明确。
  • 性能良好:原生实现,V8 等引擎高度优化。
  • 不可变性:不修改原数组,返回新数组。

注意事项

  • 不支持 IE 和部分旧版移动端浏览器(需 Babel 转译或 polyfill)。
  • 对于仅需处理单层嵌套的场景,flat()首选方案

方法 2:扩展运算符 + concat()

实现方式

利用 ES6 的扩展运算符(spread operator)将子数组作为参数传入 concat

const merged = [].concat(...arr);
console.log(merged); // [1, 2, 3, 4, 5, 6]

原理剖析

  • ...arr[[1,2], [3,4], [5,6]] 展开为三个独立参数:[1,2], [3,4], [5,6]
  • [].concat(a, b, c) 等价于合并这些数组。

优缺点

优点

  • 代码简短,一行搞定。
  • 兼容 ES6+ 环境(现代浏览器和 Node.js 广泛支持)。

缺点

  • 存在栈溢出风险:当子数组数量极大(如 > 10⁵)时,函数调用栈可能超出限制(因 concat 接收的是展开后的参数列表)。
  • 仅适用于单层嵌套,无法处理深层结构。

📌 适用建议:适合中小型数组、追求代码简洁性的场景。

方法 3:Array.prototype.reduce()

经典写法

const merged = arr.reduce((acc, subArr) => acc.concat(subArr), []);

性能优化写法(使用扩展运算符)

const merged = arr.reduce((acc, subArr) => [...acc, ...subArr], []);

深度解析

  • 第一种写法:每次调用 concat 都会创建一个新数组,时间复杂度为 O(n²),大数据量下性能较差。
  • 第二种写法:虽然避免了 concat,但 [...acc, ...subArr] 同样会复制整个累加器数组,内存开销大。

适用场景

  • 需要兼容 ES5 或更老环境(如 IE11)。
  • 需要在扁平化过程中加入自定义逻辑(如过滤、映射等):
    arr.reduce((acc, sub) => {
         
      return acc.concat(sub.filter(x => x > 2));
    }, []);
    

总结

  • 灵活性高,但性能较低
  • 除非有特殊需求,否则不建议在现代项目中作为首选。

方法 4:for...of 循环 + push(...subArr)

高性能实现

const merged = [];
for (const subArr of arr) {
   
  merged.push(...subArr);
}
console.log(merged); // [1, 2, 3, 4, 5, 6]

为什么高效?

  • 原地操作:直接向结果数组追加元素,避免中间数组创建。
  • 无函数调用开销:相比 reduceconcat,循环开销极小。
  • 内存友好:适合处理超大数组(如百万级元素)。

注意事项

  • 仍受限于扩展运算符的参数数量上限(理论上 V8 引擎限制约为 65535 个参数),若单个子数组极大(如长度 > 10⁵),可改用 push.apply 或分批处理:
    for (const subArr of arr) {
         
      Array.prototype.push.apply(merged, subArr);
    }
    

    push.apply 在极端情况下也可能栈溢出,最稳妥的方式是使用普通 for 循环逐个 push

适用场景

  • 高性能要求(如数据处理、游戏引擎、实时计算)。
  • 处理大型或不确定规模的嵌套数组。

综合对比表

方法 语法简洁性 执行性能 内存效率 兼容性 适用嵌套深度 是否修改原数组
arr.flat() ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐ ES2019+ 可控(默认1层)
[].concat(...arr) ⭐⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐ ES6+ 仅1层
reduce() + concat ⭐⭐⭐ 广泛(ES5+) 仅1层
for...of + push(...) ⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ES6+ 仅1层

注:⭐ 越多表示越优。

最佳实践建议

  1. 现代项目(支持 ES2019+)
    👉优先使用 arr.flat() —— 简洁、安全、高效。

  2. 需要兼容旧环境(如 IE11)
    使用 reduce 或手动 for 循环,并搭配 Babel 转译。

  3. 处理超大数组(> 10⁵ 元素)
    选择 for...of + push,并注意单个子数组长度限制。

  4. 需要在扁平化时附加逻辑(如去重、过滤)
    使用 reduce,灵活组合业务逻辑。

延伸思考:完全扁平化任意深度嵌套

若面对如下结构:

const deeplyNested = [1, [2, [3, [4, [5]]]]];

可封装递归函数或使用 flat(Infinity)

const flatten = arr => arr.flat(Infinity);
// 或
const flatten = arr => arr.reduce(
  (acc, val) => acc.concat(Array.isArray(val) ? flatten(val) : val),
  []
);

💡 提示:flat(Infinity) 在大多数现代引擎中已高度优化,通常优于手写递归。

结语

数组扁平化虽是一个“小问题”,却能反映出开发者对语言特性、性能边界和工程实践的理解深度。在实际项目中,应根据运行环境、数据规模、维护成本综合选择方案。对于绝大多数现代前端或 Node.js 应用,arr.flat() 已足够优雅且高效——简单,才是终极的复杂。

相关文章
|
3月前
|
JavaScript 安全 API
Vue 3 emit 参数数量不匹配问题深度解析与最佳实践
本文深入解析 Vue 3 中 `emit` 参数数量错误问题,剖析 TypeScript 类型校验机制,提供四种解决方案:修正调用参数、函数重载、运行时验证与对象语法。结合统一事件管理与组合式函数封装,助你构建类型安全、可维护的组件通信体系。
217 10
element-ui 里 el-popover 位置发生偏移
element-ui 里 el-popover 位置发生偏移
2647 0
|
算法 前端开发
经典面试题:扁平化嵌套数组
经典面试题:扁平化嵌套数组
123 0
|
3月前
|
自然语言处理 JavaScript 前端开发
全面解析 i18n:从概念到实践,再到底层原理
本文系统讲解国际化(i18n)的核心概念与实现原理,涵盖多语言文本、日期、数字、复数等处理方式,结合 i18next 与 Vue I18n 实战案例,深入剖析资源分离、环境识别与动态替换三大机制,并分享插值、格式化、CI/CD 集成等最佳实践,助力构建可扩展的全球化应用。
959 15
|
3月前
|
SQL 数据可视化 大数据
我是谁?我从哪来?我要到哪去?——聊聊数据血缘分析的“前世今生”
我是谁?我从哪来?我要到哪去?——聊聊数据血缘分析的“前世今生”
294 11
|
3月前
|
JSON 安全 JavaScript
深入浅出解析 HTTPS 原理
HTTPS是HTTP与SSL/TLS结合的安全协议,通过数字证书验证身份,利用非对称加密安全交换会话密钥,再以对称加密高效传输数据,确保通信的机密性、完整性和真实性。整个过程如同建立一条加密隧道,保障网络交互安全。
1442 16
|
3月前
|
消息中间件 缓存 前端开发
WebSocket 与 MQTT 在即时通讯中的深度对比与架构选型指南
WebSocket 是双向通信通道,适合前端实时交互;MQTT 是轻量级消息协议,支持发布/订阅与可靠传输。二者互补,常结合使用:前端通过 WebSocket 接入,后端以 MQTT 实现高并发消息分发,构建可扩展的现代即时通讯系统。
676 17
|
3月前
|
安全 Linux 网络安全
收集CentOS使用中的基础命令集锦
这些基础命令构成了CentOS管理中的骨架,熟练掌握这些命令对维护与管理系统至关重要。每个命令都具备丰富的参数选项,为了充分利用它们的功能,建议通过 `man`命令(例如 `man ls`)查看命令的手册页获取详细信息。
173 14
|
存储 前端开发 数据库
前端项目一键换肤vue+element(ColorPicker)
本文介绍了如何在前端项目中实现一键换肤功能,通过使用CSS变量和Element UI的颜色选择器组件(ColorPicker),并结合Vuex进行状态管理和持久化,实现主题颜色的动态切换。
504 3
前端项目一键换肤vue+element(ColorPicker)