似乎没有办法用另一个数组扩展现有的 JavaScript 数组,即模拟 Python 的extend
方法。
我想实现以下目标:
>>> a = [1, 2]
[1, 2]
>>> b = [3, 4, 5]
[3, 4, 5]
>>> SOMETHING HERE
>>> a
[1, 2, 3, 4, 5]
我知道有一个a.concat(b)
方法,但它创建了一个新数组,而不是简单地扩展第一个数组。我想要一个算法,当a
明显大于b
(即不复制a
的算法),它可以有效地工作。
注意:这不是如何将某些内容附加到数组的副本? - 这里的目标是将一个数组的全部内容添加到另一个数组中,并 “就地” 执行,即不复制扩展数组的所有元素。
.push
方法可以使用多个参数。您可以使用spread 运算符将第二个数组的所有元素作为参数传递给.push
:
>>> a.push(...b)
如果您的浏览器不支持 ECMAScript 6,则可以使用.apply
:
>>> a.push.apply(a, b)
或许,如果你认为它更清楚:
>>> Array.prototype.push.apply(a,b)
请注意,如果阵列b
太长,所有这些解决方案都会因堆栈溢出错误而失败(麻烦从大约 100,000 个元素开始,具体取决于浏览器)。如果你不能保证b
足够短,你应该使用另一个答案中描述的基于循环的标准技术。
更新 2018 年 : 更好的答案是我的新答案 : a.push(...b)
。不要再投票了,因为它从来没有真正回答过这个问题,但这是 2015 年的第一次攻击谷歌的黑客攻击:)
对于那些只是搜索 “JavaScript 数组扩展” 的人来到这里,你可以很好地使用Array.concat
。
var a = [1, 2, 3];
a = a.concat([5, 4, 3]);
Concat 将返回一个新数组的副本,因为线程启动程序不想要。但你可能不在乎(当然对于大多数用途来说这样会很好)。
以扩展运算符的形式还有一些不错的 ECMAScript 6 糖:
const a = [1, 2, 3];
const b = [...a, 5, 4, 3];
(它也复制。)
您应该使用基于循环的技术。此页面上基于使用.apply
其他答案可能会因大型阵列而失败。
一个相当简洁的基于循环的实现是:
Array.prototype.extend = function (other_array) {
/* You should include a test to check whether other_array really is an array */
other_array.forEach(function(v) {this.push(v)}, this);
}
然后,您可以执行以下操作:
var a = [1,2,3];
var b = [5,4,3];
a.extend(b);
当我们追加的阵列很大时, DzinX 的答案 (使用 push.apply)和其他基于.apply
的方法都会失败(测试显示,对我来说,Chrome 中大约 150,000 个条目,Firefox 中大于 500,000 个条目)。您可以在此 jsperf 中看到此错误。
发生错误是因为当使用大数组作为第二个参数调用'Function.prototype.apply' 时,超出了调用堆栈大小。 (MDN 记录了使用Function.prototype.apply超出调用堆栈大小的危险 - 请参阅标题为 “应用和内置函数” 的部分。)
要与本页面上的其他答案进行速度比较,请查看此 jsperf (感谢 EaterOfCode)。基于循环的实现与使用Array.push.apply
速度类似,但往往比Array.slice.apply
慢一点。
有趣的是,如果你追加的数组是稀疏的,那么上面基于forEach
的方法可以利用稀疏性并优于基于.apply
的方法; 如果你想亲自测试一下,请查看这个 jsperf 。
顺便说一句,不要被诱惑(像我一样!)进一步缩短 forEach 实现:
Array.prototype.extend = function (array) {
array.forEach(this.push, this);
}
因为这会产生垃圾结果!为什么?因为Array.prototype.forEach
为它调用的函数提供了三个参数 - 这些参数是:(element_value,element_index,source_array)。如果你使用 “forEach(this.push,this)”,那么所有这些都会被推送到你的第一个数组,用于forEach
每次迭代!