JavaScript中合并数组的N种方法

作者:听澜

时间:2026-10-28

浏览:573

本文深入探讨JavaScript中合并数组的多种实现方式,包括concat、循环push/unshift、reduce以及apply技巧。重点分析各方法在内存占用、执行效率及调用栈限制方面的差异,并提供处理大数组时的分段合并策略,帮助开发者根据实际场景选择最优方案。

文章目录

JavaScript数组合并概述

本文介绍几种在JavaScript中合并数组的实用技巧,旨在对比不同合并方法的优缺点。

首先设定以下测试场景:

代码如下:

var a = [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ];
var b = [ "foo", "bar", "baz", "bam", "bun", "fun" ];

预期的合并结果应为:


代码如下:

[
1, 2, 3, 4, 5, 6, 7, 8, 9,
"foo", "bar", "baz", "bam" "bun", "fun"
]

concat方法及其内存开销

concat(..)

这是最常规的合并方式:

代码如下:

var c = a.concat( b );
a; // [1,2,3,4,5,6,7,8,9]
b; // ["foo","bar","baz","bam","bun","fun"]
c; // [1,2,3,4,5,6,7,8,9,"foo","bar","baz","bam","bun","fun"]

如上所示,c是一个包含a和b所有元素的新数组,而a和b保持不变。这种方法简洁直观。

然而,如果数组a包含10,000个元素,数组b也有1万个元素,合并后的c将拥有2万个元素,导致内存使用量暂时翻倍。

有人可能会说,让垃圾回收机制处理,将a和b设为null即可解决问题:

代码如下:

a = b = null; // 'a'和'b'就被回收了

对于元素较少的小数组,这确实不是问题。但在内存受限的系统或需要频繁执行此操作的场景中,这种内存翻倍的现象仍有优化空间。

循环插入与push/unshift技巧

循环插入

我们可以尝试将数组b的内容逐个复制到数组a中,使用Array#push(..)方法:

代码如下:

// `b` onto `a`
for (var i=0; i < b.length; i++) {
a.push( b[i] );
}
a; // [1,2,3,4,5,6,7,8,9,"foo","bar","baz","bam","bun","fun"]
b = null;

此时,数组a包含了数组b的内容。

从内存角度看,这种方式似乎更优。

但如果数组a较小,为了节省内存和提高速度,你可能希望将较小的数组a插入到数组b的前面。只需将push(..)替换为unshift(..)即可:

代码如下:

// `a` into `b`:
for (var i=a.length-1; i >= 0; i--) {
b.unshift( a[i] );
}
b; // [1,2,3,4,5,6,7,8,9,"foo","bar","baz","bam","bun","fun"]

函数式方法与apply技巧

功能技巧

虽然for循环可行,但代码可读性和维护性较差。我们能否找到更好的方案?

第一次尝试是使用Array#reduce:

代码如下:

// `b` onto `a`:
a = b.reduce( function(coll,item){
coll.push( item );
return coll;
}, a );
a; // [1,2,3,4,5,6,7,8,9,"foo","bar","baz","bam","bun","fun"]
// or `a` into `b`:
b = a.reduceRight( function(coll,item){
 coll.unshift( item );
 return coll;
}, b );
b; // [1,2,3,4,5,6,7,8,9,"foo","bar","baz","bam","bun","fun"]

Array#reduce(..) 和 Array#reduceRight(..)虽然可行,但略显繁琐。ES6的箭头函数可以减少部分代码量,但仍需为每个元素调用一次函数,并非最完美方案。

试试这种方法:

代码如下:

// `b` onto `a`:
a.push.apply( a, b );
a; // [1,2,3,4,5,6,7,8,9,"foo","bar","baz","bam","bun","fun"]
// or `a` into `b`:
b.unshift.apply( b, a );
b; // [1,2,3,4,5,6,7,8,9,"foo","bar","baz","bam","bun","fun"]

这看起来更简洁。特别是使用unshift(..)时,无需担心元素顺序反转的问题。ES6的spread操作符写法更为优雅:a.push( ...b ) 或 b.unshift( ...a )。

处理大数组的长度限制与分段策略

数组最大长度限制

上述方法面临的主要问题是内存增长(尽管是暂时的),因为元素是通过函数调用复制到调用栈中的。此外,不同的JavaScript引擎对拷贝数据的长度有限制。如果数组包含一百万个元素,很可能会超出push(...)或unshift(...)允许的调用栈限制。虽然处理几千个元素通常没问题,但必须注意不要超过合理的长度限值。

注意:你可以尝试使用splice(...),它同样存在push(...)和unshift(...)那样的限制问题。

有一种方法可以规避这种最大长度限制。

代码如下:

function combineInto(a,b) {
var len = a.length;
for (var i=0; i < len; i=i+5000) {
b.unshift.apply( b, a.slice( i, i+5000 ) );
    }
}

这种写法虽然牺牲了部分可读性,但能有效避免栈溢出风险,尽管代码看起来可能变得复杂。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章