JavaScript数组合并的多种方法:concat、push与性能权衡
本文深入探讨JavaScript中合并两个数组的多种实现方式,包括基础的concat方法、循环使用push/unshift,以及利用reduce和apply优化的技巧。重点分析大数组场景下的内存占用、调用栈限制及性能差异,并提供规避长度限制的分块处理方案。
JavaScript数组合并概述
本文简要介绍JavaScript数组操作的一些实用技巧,重点讨论如何结合或合并两个JS数组,并分析每种方法的优缺点。
首先设定以下测试场景:
代码如下:
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也有一万个元素,c将拥有2万个元素,导致a和b的内存使用量暂时翻倍。
“没问题!”,你可能会说,将它们设为null以触发垃圾回收:
a = b = null; // 'a'和'b'就被回收了
对于元素较少的小数组,这并非问题。但在内存受限系统中频繁处理大数组时,这种内存翻倍策略仍有改进空间。接下来介绍循环插入法。
循环插入与push/unshift技巧
我们可以尝试将一个数组的内容复制到另一个,使用: 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"]利用reduce和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。
处理数组长度限制与内存问题
数组最大长度限制
上述方法的主要问题是内存使用量暂时翻倍(尽管是暂时的!)。被追加的内容本质上是通过函数调用将元素复制到堆栈中。此外,不同的JS引擎对拷贝数据的长度有限制。
因此,如果数组包含一百万个元素,你肯定会超出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 ) );
}
}这段代码的可读性有所倒退。就这样吧,可能会越改越差,呵。
tween.js 内存泄漏预防:怎么正确停止动画与清理对象
2026-11-05 15:52:56
Vue或React项目中为何不建议直接使用JS刷新页面
2026-11-05 13:57:30
哪里可以找到高质量的免费JS脚本资源?
2026-11-05 13:15:03
JS数组对象怎么根据指定ID字段去重
2026-11-04 21:41:10
JS数组去重方法性能对比:Set、Map与Filter谁更快
2026-11-04 16:14:15
