# 数组合并
## 前言
当面试官问你如何用js做数组合并,不要想当然的回答用`concat`方法。这是不合格的。面试官想听的是多种的方法以及分析其优劣,下面将列出js数组合并的方法并分析优劣
## concat方法
这是最常见的用法。例如有这样一个数组
<pre>var a = [1,2,3,4,5]
var b =['a', 'b','c','d','e']
</pre>
拼接出来的结果是
`var c = a.concat(b) //[1, 2, 3, 4, 5, "a", "b", "c", "d", "e"]`
concat会返回一个<b>全新的数组</b>c, 不会改变`a`和`b`数组,但他们已经没用了。
现在深入解析concat方法的问题。
如果数组`a`和`b` 都有10000个以上元素。 那么c就有20000个以上元素,这种方式占用了2倍的内存
如果你说将 数组`a`,`b`空置等待垃圾回收不就可以了吗 `a = b = null`
但如果是小数组,那自然没问题,但对大型数组或者需要多次重复处理时,内存就被限制了。还需要进行优化。
## 循环插入
可以把一个数组加入到另外一个数组中,使用Array.push()
<pre>//将数组b插入到a中
for(var i = 0; i < b.length; i++) {
a.push(b[i])
}
b = null
a //[1, 2, 3, 4, 5, "a", "b", "c", "d", "e"]
</pre>
现在 `a`中存放了2个原始数组的内容, 看样子对内存优化做的不错。
另外,如果数组 `b`很大,`a`很小。 出于内存和速度的考虑,应该把较小的`a` 插入`b`中。 并且用`unshift()`方法代替 `push()` 即可, 对应的也要从大到小进行循环遍历:
<pre>// 将数组 a 插入 b
for (var i= a.length; i >= 0; i--) {
b.unshift( a[i] );
}
b; //["a", "b", "c", "d", "e",1, 2, 3, 4, 5]
a = null
</pre>
## Array.reduce
上面的for循环 很土,而且难以维护。当然有高大上的
<pre>//将 b 插入 a
a = b.reduce(function(coll, item) {
coll.push(item)
return coll
},a)
a //[1, 2, 3, 4, 5, "a", "b", "c", "d", "e"]
</pre>
<pre>
a = b.reduceRight(function(coll, item) {
coll.push(item)
return coll
},a)
a // [1, 2, 3, 4, 5, "e", "d", "c", "b", "a"]
</pre>
(注意回调函数后面别漏了数组a, 意思是:以数组a为叠加的初始值)
`Arrar.reduce` 和`Array.reduceRight` 高大上得来又有点笨重, 而且一般人记不住,下面还有更高大上的方法
## apply 和 es6展开运算符
<pre>//b 插入a
a.unshift.apply(a, b)
console.log(a) //["a", "b", "c", "d", "e", 1, 2, 3, 4, 5]
a.push.apply(a,b)
a // [1, 2, 3, 4, 5, "a", "b", "c", "d", "e"]
a.push(...b)
a //[1, 2, 3, 4, 5, "a", "b", "c", "d", "e"]
</pre>
是不是big更高了。但是,事实上这种方法还是太乐观了. 在这两种情况下,不管是将 a 或 b 传递给 apply() 作为第二个参数(apply方式调用Function时第一个参数在内部变成this,即context,上下文,作用域), 还是使用 ... 展开运算符的方式, 实际上数组都会被打散成为函数的 arguments .
第一个主要的问题是,占用了双倍的内存(当然,是临时的!),因为需要将数组复制到函数栈之中. 此外,不同的JS引擎有不同的实现算法,可能会限制了函数可以传递的参数数量.
如果数组添加了一百万个元素, 那一定会超过函数栈所允许的大小, 不管是push() 或 unshift()调用. 这种方式只在几千个元素时可用,所以必须限制其不能超过一定范围.
## 总结
有很多变通的手法,但他们都有不同的优缺点,需要根据实际情况来选择.