是否可以更快地直接访问javascript数组?

时间:2009-08-04 20:38:35

标签: javascript arrays performance

我正在阅读一篇文章:Optimizing JavaScript for Execution Speed

还有一节说:

使用此代码:

for (var i = 0; (p = document.getElementsByTagName("P")[i]); i++)

代替:

nl = document.getElementsByTagName("P");

for (var i = 0; i < nl.length; i++)
{
    p = nl[i];
}

出于性能原因。

根据文章,我总是使用“错误”的方式,但是,我错了还是文章错了?

10 个答案:

答案 0 :(得分:5)

“我们应该忘记效率低下,大约97%的时间说:过早的优化是所有邪恶的根源。”

- Donald Knuth


我个人会用你的方式,因为它更易读,更容易维护。然后我会使用诸如YSlow之类的工具来分析代码并消除性能瓶颈。

答案 1 :(得分:3)

好问题 - 我假设不调用函数getElementsByTagName()每个循环都会节省时间。我可以看到这更快 - 你没有检查数组的长度,只是分配了值。

var p;
var ps = document.getElementsByTagName("P");

for (var i = 0; (p=ps[i]); i++) {
  //...
}

当然,这也假设数组中没有任何值计算为“false”。可能包含0的数字数组将破坏此循环。

答案 2 :(得分:3)

如果你从C#这样的语言看它,你会期望第二个语句更有效率,但C#不是解释器语言。

正如指南所述:您的浏览器已经过优化,可以从实时列表中检索正确的节点,并且比从您在变量中定义的“缓存”中检索它们要快得多。此外,您必须确定每次迭代的长度,同时可能会导致性能损失。

解释器语言与编译语言的反应不同,它们以不同的方式进行优化。

答案 3 :(得分:1)

有趣。其他参考文献似乎支持NodeLists相对较重的观点。

问题是......开销是多少?是否足以打扰?我不是过早优化的粉丝。然而,这是一个有趣的案例,因为它不仅仅是受影响的迭代成本,还有额外的开销,因为NodeList必须与DOM的任何更改保持同步。

没有进一步的证据我倾向于相信这篇文章。

答案 4 :(得分:1)

不,它不会更快。实际上它几乎完全是胡说八道,因为对于for循环的每一步,你都称之为“getElementsByTagName”,这是一个耗时的功能。

理想的循环如下:

nl = document.getElementsByTagName("P");

for (var i = nl.length-1; i >= 0; i--)
{
    p = nl[i];
}

编辑: 我实际上测试了你在Firebug中使用console.time给出的那两个例子,虽然第一个花了1毫秒而第二个花了0毫秒=)

答案 5 :(得分:0)

将它直接分配给变量是有意义的。写作也可能更快。我会说这篇文章可能有些道理。

不必每次都获取长度,而是检查i,然后分配变量,只需检查p是否能够设置。这样可以清理代码,实际上可能更快。

答案 6 :(得分:0)

我通常会这样做(在for循环之外移动长度测试)

var nl = document.getElementsByTagName("p");
var nll = nl.length;
for(var i=0;i<nll;i++){
    p = nl[i];
}

或紧凑......

var nl = document.getElementsByTagName("p");
for(var i=0,nll=nl.length;i<nll;i++){
    p = nl[i];
}

假设访问期间长度不变(在我的情况下没有)

但是我会说在文章构思上运行一系列性能测试将是明确的答案。

答案 7 :(得分:0)

该文章的作者写道:

  

在大多数情况下,这比缓存NodeList更快。在第二个示例中,浏览器不需要创建节点列表对象。它只需要在那个时刻找到索引i处的元素。

一如既往地取决于此。也许它取决于NodeList的元素数量。 对我来说,如果元素的数量可以改变,这种方法是不安全的,这可能导致并且索引越界。

答案 8 :(得分:0)

从您链接的文章:

  

在第二个示例中,浏览器不需要创建节点列表对象。它只需要在那个时刻找到索引i处的元素。

这是胡说八道。在第一个示例中,创建节点列表,并将对它的引用保存在变量中。如果发生导致节点列表发生变化的事情 - 比如你删除一个段落 - 那么浏览器必须做一些工作来更新节点列表。但是,如果您的代码不会导致列表更改,则不会出现问题。

在第二个示例中,浏览器不必创建节点列表,每次通过循环创建节点列表,然后在索引i处找到元素。事实上,对节点列表的引用永远不会分配给变量并不意味着不必像创作者那样创建列表。对象创建很昂贵(无论作者如何评价浏览器“为此进行优化”),因此对于许多应用程序来说这将是一个巨大的性能损失。

优化始终取决于应用程序遇到的实际实际使用情况。诸如此类的文章不应被视为“始终以这种方式工作”,而是作为技术的集合,在某些特定情况下,任何一种技术都可能具有价值。在我看来,第二个例子不太容易理解,仅此一个就是把它放在特技手段中,只有在特定情况下有证明的好处才能使用。

(坦率地说,我也不相信使用变量名称如“nl”的程序员提供的建议。如果他在编写教程时懒得使用有意义的名字,我很高兴我不必维护他的生产代码。)

答案 9 :(得分:0)

当实际测试更准确时讨论理论是没有价值的,并且通过比较第二种方法显然更快。

以下是基准测试的示例代码

var start1 = new Date().getTime();
for (var j= 0; j < 500000; j++){
   for (var i = 0; (p = document.getElementsByTagName("P")[i]); i++);
}
var end1 = new Date().getTime() - start1;

var start2 = new Date().getTime();
for (var j= 0; j < 500000; j++){
    nl = document.getElementsByTagName("P");

    for (var i = 0; i < nl.length; i++)
    {
       p = nl[i];
    }
}
var end2 = new Date().getTime() - start2;

alert("first:" + end1 + "\nSecond:" + end2);

在chrome中,第一种方法需要2324ms,而第二种方法需要1986ms。

但请注意,对于500,000次迭代,差异只有300ms,所以我根本不会理会这一点。