array.push(element)与array[array.length] = element的区别

5

我想知道为什么要选择

array.push(element)

over

array[array.length] = element

或反之亦然。

这里有一个简单的示例,其中我有一个数字数组,我想创建一个新的数组,其中包含这些数字乘以2:

var numbers = [5, 7, 20, 3, 13];

var arr1 = [];
var len = numbers.length;
for(var i = 0; i < len; i++){
    arr1.push(numbers[i] * 2);
}
alert(arr1);

var arr2 = [];
for(var i = 0; i < len; i++){
    arr2[arr2.length] = numbers[i] * 2;
}
alert(arr2);

1
它们都提供了为什么使用任一方法的原因。 - Ian
1
没有必要每次计算数组的长度-你可以使用i,或者如果数组不是每个i都增加,则增加另一个变量。 - kennebec
这里有很多混淆。看看这个链接:https://jsperf.com/array-direct-assignment-vs-push - Manohar Reddy Poreddy
1个回答

1

目前使用最快速的JavaScript技术,同时使用最少的代码,是先存储最后一个元素,从而分配完整的数组索引,然后向0计数并存储元素,从而利用附近的内存存储位置并最小化缓存未命中。

var arr3 = [];
for (var i = len; i>0;){
    i--;
    arr2[i] = numbers[i] * 2;
}
alert(arr2);

请注意,如果在JavaScript引擎的视角下,存储的元素数量足够大,则数组将被创建为“稀疏”数组,并且永远不会转换为常规平面数组。
是的,我可以支持这一点。唯一的问题是JavaScript优化器非常积极地抛弃未使用的计算。因此,为了公平地计算结果,所有结果都必须被存储(暂时)。我认为已经过时的另一个优化是使用new Array(*length*)预初始化数组。那是一个老掉牙的技巧,一度没有任何区别,但在极端JavaScript引擎优化的时代,它似乎再次有所改进。
<script>
function arrayFwd(set) {
var x = [];
for (var i = 0; i<set.length; i++)
x[x.length] = set[i];
return x;
}

function arrayRev(set) {
var x = new Array(set.length);
for (var i = set.length; i>0;) {
i--;
x[i] = set[i];
}
return x;
}

function arrayPush(set) {
var x = [];
for (var i = 0; i<set.length; i++)
x.push(set[i]);
return x;
}

results = []; /* we'll store the results so that
optimizers don't realize the results are not used
and thus skip the function's work completely */
function timer(f, n) {
return function(x) {
var n1 = new Date(), i = n;
do { results.push(f(x)); } while (i-- > 0); // do something here
return (new Date() - n1)/n;
};
}

set = [];
for (i=0; i<4096; i++)
set[i] = (i)*(i+1)/2;

timers = {
forward: timer(arrayFwd, 500),
backward: timer(arrayRev, 500),
push: timer(arrayPush, 500)
};
for (k in timers) {
document.write(k, ' = ', timers[k](set), ' ms<br />');
}
</script>

Opera 12.15:

前进 = 0.12 毫秒 后退 = 0.04 毫秒 推送 = 0.09 毫秒

Chrome(最新版本,v27):

前进 = 0.07 毫秒 后退 = 0.022 毫秒 推送 = 0.064 毫秒

(作为比较,当结果未存储时,Chrome 产生以下数字: 前进 = 0.032 毫秒 后退 = 0.008 毫秒 推送 = 0.022 毫秒

这比正向数组快近四倍,比push快近三倍。)

IE 10: 前进 = 0.028 毫秒 后退 = 0.012 毫秒 推送 = 0.038 毫秒

奇怪的是,Firefox 仍然显示推送更快。当使用push时,Firefox 应该在后台进行一些代码重写,因为在纯、未增强的 JavaScript 性能方面,访问属性和调用函数都比使用数组索引慢。


3
你能证实这个吗? - Tom
1
实际上,它会先创建一个稀疏数组,改变数组的长度,然后倒序填充每个元素,只有当填充完0索引时,才将其转换为“常规”数组。在我的测试中,当分配数组时,这种方法实际上比正向循环更慢。此外,你的代码并没有完全优化。 - Qantas 94 Heavy
1
@user2246674 在不同的浏览器中没有一致性,所以尝试去做这件事情并不值得。数组大小、浏览器和[]的使用方式都会导致差异。有些人在循环中使用[i],有些人使用[array.length],它们有不同的含义。 - Ian
在询问“最快的方法”时,请求备份+1 - leopic
2
@Qantas94Heavy 你是对的,今天这个方法和我上次测试时不同了。以前,反过来做总是更优的选择。现在似乎浏览器仍然会像你说的那样创建稀疏数组,特别是 Firefox,在 push 方面性能更快。原则上,反向操作应该是最快的,因为它做的事情与初始化完整数组并存储相邻元素相同。但由于 JavaScript 引擎进行了优化,代码中写的不是实际发生的背后的事情。 - Joseph Myers
在我的许多基准测试中,正向遍历数组通常比 i-- 更快,这可能是因为引擎优化期望您以这种方式执行操作。 - Ben Gubler

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接