javascript技巧

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript技巧 > JavaScript数组扁平化

一文解析JavaScript中数组扁平化的各种实现方法

作者:yyt_

在现代前端开发中,数组操作是日常编码中最常见的任务之一,本文将带你全面了解 JavaScript 中数组扁平化的各种方法,帮助你构建完整的知识体系

在现代前端开发中,数组操作是日常编码中最常见的任务之一。而在处理复杂数据结构时,我们经常会遇到“嵌套数组”(即高维数组)的场景。例如,后端返回的数据结构可能是多层嵌套的,我们需要将其“拍平”为一维数组以便于渲染或进一步处理。这种将多层嵌套数组转换为单层数组的过程,就被称为 数组扁平化(Array Flattening)

本文将带你全面了解 JavaScript 中数组扁平化的各种方法,包括原生 API 的使用、递归实现、reduce 高阶函数应用、利用 toStringsplit 的巧妙技巧,以及基于展开运算符的循环优化方案。我们将深入剖析每种方法的原理、优缺点和适用场景,帮助你构建完整的知识体系。

一、什么是数组扁平化

数组扁平化,顾名思义,就是把一个嵌套多层的数组“压平”成一个只有一层的一维数组。例如:

const nestedArr = [1, [2, 3, [4, 5]], 6];
// 扁平化后应得到:
// [1, 2, 3, 4, 5, 6]

这个问题看似简单,但在实际项目中非常常见。比如你在处理树形菜单、评论回复结构、文件目录层级等数据时,都可能需要对嵌套数组进行扁平化处理。

二、使用原生flat()方法(推荐方式)

ES2019 引入了 Array.prototype.flat() 方法,使得数组扁平化变得极其简单和直观。

基本语法

arr.flat([depth])

示例代码

const arr = [1, [2, 3, [1]]];

console.log(arr.flat());           // [1, 2, 3, [1]]     → 只展开一层
console.log(arr.flat(2));          // [1, 2, 3, 1]       → 展开两层
console.log(arr.flat(Infinity));   // [1, 2, 3, 1]       → 完全展开

特点总结

注意:flat() 不会改变原数组,而是返回一个新的扁平化数组。

三、递归实现:最经典的思路

如果你不能使用 flat()(比如兼容老版本浏览器),或者想深入理解其内部机制,那么递归是一个经典且直观的解决方案。

基础递归版本

function flatten(arr) {
    let res = [];
    for (let i = 0; i < arr.length; i++) {
        if (Array.isArray(arr[i])) {
            res = res.concat(flatten(arr[i])); // 递归处理子数组
        } else {
            res.push(arr[i]); // 非数组元素直接加入结果
        }
    }
    return res;
}

// 测试
const arr = [1, [2, 3, [1]]];
console.log(flatten(arr)); // [1, 2, 3, 1]

分析

使用 for 循环遍历每个元素。

判断是否为数组:是 → 递归调用;否 → 直接推入结果数组。

利用 concat 合并递归结果。

缺点

每次 concat 都会创建新数组,性能略低。

递归深度过大可能导致栈溢出(极端情况)。

四、使用reduce+ 递归:函数式编程风格

利用 reduce 可以写出更优雅、更具函数式风格的扁平化函数。

实现方式

function flatten(arr) {
    return arr.reduce((pre, cur) => {
        return pre.concat(Array.isArray(cur) ? flatten(cur) : cur);
    }, []);
}

解析

优点

五、利用toString()+split()的“黑科技”技巧

这是一个非常巧妙但需要谨慎使用的技巧,适用于数组中只包含数字或字符串基本类型的情况。

实现原理

JavaScript 中,数组的 toString() 方法会递归地将每个元素转为字符串,并用逗号连接

const arr = [1, [2, 3, [1]]];
console.log(arr.toString()); // "1,2,3,1"

我们可以利用这一点,先转成字符串,再用 split(',') 分割,最后通过 +item 转回数字。

实现代码

function flatten(arr) {
    return arr.toString().split(',').map(item => +item);
}

// 测试
const arr = [1, [2, 3, [1]]];
console.log(flatten(arr)); // [1, 2, 3, 1]

优点

缺点

所以这个方法虽然巧妙,但不适合通用场景,仅作为面试中的“技巧”了解即可。

六、使用while循环 +concat+ 展开运算符(性能优化版)

这种方法避免了递归调用,采用循环逐步“拍平”数组,适合处理深层嵌套且希望避免栈溢出的场景。

实现方式

function flatten(arr) {
    while (arr.some(item => Array.isArray(item))) {
        arr = [].concat(...arr);
    }
    return arr;
}

原理解析

举个例子:

[].concat(...[1, [2, 3, [1]]])
// 等价于
[].concat(1, [2, 3, [1]])
// → [1, 2, 3, [1]]  → 拍平了一层

然后继续循环,直到没有嵌套为止。

优点

缺点

七、对比总结:各种方法的适用场景

方法优点缺点推荐场景
arr.flat(Infinity)简洁、标准、安全IE 不支持生产环境首选
递归 + for逻辑清晰,易理解性能一般,可能栈溢出学习理解原理
reduce + 递归函数式风格,优雅同上偏好函数式编程
toString + split代码短,性能好类型受限,不通用面试技巧
while + concat + ...非递归,避免栈溢出内存占用高深层嵌套处理

八、扩展思考:如何实现深度可控的扁平化

有时候我们并不想完全拍平,而是只想展开指定层数。可以仿照 flat(depth) 实现一个通用函数:

function flattenDepth(arr, depth = 1) {
    if (depth === 0) return arr.slice(); // 深度为0,直接返回副本

    let result = [];
    for (let item of arr) {
        if (Array.isArray(item) && depth > 0) {
            result.push(...flattenDepth(item, depth - 1));
        } else {
            result.push(item);
        }
    }
    return result;
}

// 测试
const arr = [1, [2, 3, [4, 5, [6]]]];
console.log(flattenDepth(arr, 1)); // [1, 2, 3, [4, 5, [6]]]
console.log(flattenDepth(arr, 2)); // [1, 2, 3, 4, 5, [6]]
console.log(flattenDepth(arr, Infinity)); // [1, 2, 3, 4, 5, 6]

九、结语

小贴士:如果你的项目需要兼容老旧浏览器,可以使用 Babel 转译 flat(),或手动引入 polyfill:

// Polyfill for Array.prototype.flat
if (!Array.prototype.flat) {
    Array.prototype.flat = function(depth = 1) {
        return this.reduce((acc, val) => 
            Array.isArray(val) && depth > 0
                ? acc.concat(val.flat(depth - 1))
                : acc.concat(val)
        , []);
    };
}

这样就能在任何环境中愉快地使用 flat() 了!

到此这篇关于一文解析JavaScript中数组扁平化的各种实现方法的文章就介绍到这了,更多相关JavaScript数组扁平化内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文