Javascript:长度方法有效吗?

时间:2012-03-06 21:30:18

标签: javascript arrays immutability

我正在做一些javascript编码,我想知道长度方法是“预先计算”,还是由JS引擎记住。

所以,问题是:

如果我经常检查数组长度,并假设我没有更改它(通过闭包使其不可变),我应该预先计算长度方法并将其存储在某个变量中吗?

谢谢!

5 个答案:

答案 0 :(得分:24)

与往常一样,答案是“它取决于”。

Let's test native arrays with a million-element array:

for (var i = 0; i < arr.length; i++);

var len=arr.length;
for (var i = 0; i < len; i++);

http://josh3736.net/images/arrlen.png

Chrome和Firefox优化属性访问器与将长度复制到局部变量一样高效。 IE和Opera没有,而且速度慢了50%。

但是,请记住,测试结果'“ops / second”表示每秒通过一百万个元素的数组进行完整迭代的次数。

为了正确看待这一点,即使在IE8中(这一组中表现最差的) - 在属性访问和局部变量(分别)上得分为.44和3.9 - 每次迭代惩罚很少2μs 即可。使用array.length迭代超过一千个项目只会花费额外的2毫秒。换句话说:提防过早优化

答案 1 :(得分:13)

实际数组的长度不是即时计算的。它存储为数组数据结构的一部分,因此访问它不仅仅涉及获取值(没有计算)。因此,它通常与检索对象的任何固定属性一样快。正如您在此性能测试中所看到的,检索数组长度和检索对象属性之间基本没有区别:

http://jsperf.com/length-comparisons

对此的一个例外是DOM从getElementsByTagName()getElementsByClassName()等函数返回的nodeList对象。在这些中,访问length属性通常要慢得多。这可能是因为这些nodeList对象不是真正的javascript对象,并且每次从这些对象访问某些东西时,Javascript和本机代码之间可能存在桥接。在这种情况下,将长度缓存到局部变量而不是在nodeList的循环中重复使用它会快很多(快10-100倍)。我已将其添加到长度比较中,您可以看到它的速度有多慢。

在某些浏览器中,将长度放入局部变量并从那里使用它会更快,如果你将反复引用它(如在循环中)。这是上面jsperf测试的性能图:

答案 2 :(得分:7)

所有主要的解释器都为本机数组提供了有效的访问器,但不适用于像NodeList这样的类似数组的对象。

"Efficient looping in Javascript"

Test / Browser                Firefox 2.0 Opera 9.1   Internet Explorer 6
Native For-Loop               155 (ms)    121 (ms)    160 (ms)
...
Improved Native While-Loop    120 (ms)    100 (ms)    110 (ms)

"Efficient JavaScript code"建议

for( var i = 0; i < document.getElementsByTagName('tr').length; i++ ) {
  document.getElementsByTagName('tr')[i].className = 'newclass';
  document.getElementsByTagName('tr')[i].style.color = 'red';
  ...
}
     
var rows = document.getElementsByTagName('tr');
for( var i = 0; i < rows.length; i++ ) {
  rows[i].className = 'newclass';
  rows[i].style.color = 'red';
  ...
}
     

这些都不是有效的。 getElementsByTagName返回动态对象,而不是静态数组。每次检查循环条件时,Opera都必须重新评估对象,并计算它引用的元素数量,以便计算出length属性。这比检查静态数字需要更多的时间。

答案 3 :(得分:2)

由于属性查找速度的原因,可能会通过在局部变量中缓存长度来实现适度的速度提升。这可能会或可能不会忽略,具体取决于JS引擎如何JIT代码。

有关基本的JSperf测试用例,请参阅http://jsperf.com/for-loop-caching

答案 4 :(得分:1)

对于任何不会操纵其长度的集合类型对象(例如任何不可变集合),最好缓存其长度以获得更好的性能。

var elems = document.getElementsByName("tst");
var elemsLen = elems.length;
var i;
for(i = 0; i < elemsLen; ++i)
{
  // work with elems... example:
  // elems[i].selected = false;
}
elems = [10,20,30,40,50,60,70,80,90,100];
elemsLen = elems.length;
for(i = 0; i < elemsLen; ++i)
{
  // work with elems... example:
  // elems[i] = elems[i] / 10;
}