协慌网

登录 贡献 社区

如何使用另一个数组扩展现有 JavaScript 数组,而无需创建新数组

似乎没有办法用另一个数组扩展现有的 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每次迭代!