【问题标题】:array.push(element) vs array[array.length] = element [duplicate]array.push(element) vs array[array.length] = element [重复]
【发布时间】:2013-06-01 21:38:13
【问题描述】:

我想知道是否有选择的理由

array.push(element)

结束

array[array.length] = element

反之亦然。

Here's a simple example 我有一个数字数组,我想将这些数字乘以 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);

【问题讨论】:

  • 他们都提供了使用任何一种方法的理由
  • 没有理由每次都计算数组长度——你可以使用i,或者如果数组不为每个i递增,则增加另一个变量。
  • 这里有很多混乱。看看这个:jsperf.com/array-direct-assignment-vs-push

标签: javascript arrays optimization


【解决方案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>

歌剧 12.15:

向前 = 0.12 毫秒 向后 = 0.04 毫秒 推送 = 0.09 毫秒

Chrome(最新,v27):

向前 = 0.07 毫秒 向后 = 0.022 毫秒 推送 = 0.064 毫秒

(为了比较,当结果未存储时,Chrome 会生成这些数字: 正向 = 0.032 毫秒 向后 = 0.008 毫秒 推送 = 0.022 毫秒

这比向前执行数组快了近四倍,比执行推送快了近三倍。)

IE 10: 正向 = 0.028 毫秒 向后 = 0.012 毫秒 推送 = 0.038 毫秒

奇怪的是,Firefox 仍然显示推送速度更快。使用推送时,Firefox 必须在后台进行一些代码重写,因为就纯粹的、未增强的 JavaScript 性能而言,访问属性和调用函数都比使用数组索引慢。

【讨论】:

  • 你能备份一下吗?
  • 其实它会先创建一个稀疏数组,改变数组的长度然后倒序填充每个元素,然后只有当0索引被填充时,然后它被转换为“常规”数组。在我的测试中,分配数组时,这种方法实际上比前向循环慢。此外,您的代码并未完全优化。
  • @user2246674 浏览器之间确实没有一致性,因此不值得尝试。数组的大小、浏览器和[] 的使用方式之间存在差异。有的人循环使用[i],有的人使用[array.length],含义不同
  • +1 请求支持“最快的方法”
  • @Qantas94Heavy 你说得对,今天的工作方式与我上次测试时不同。以前,倒着做总是大赢家。现在看来,浏览器仍然像你说的那样创建一个稀疏数组,特别是 Firefox,它在推送时执行得更快。原则上,向后执行应该是最快的,因为它与初始化完整数组然后存储相邻元素的操作相同。但是由于 JavaScript 引擎优化,代码中写的并不是后台实际发生的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-10-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-12
相关资源
最近更新 更多