读取一个数组的`length`属性真的很贵在JavaScript中的操作?

我总是假设在JavaScript中caching数组的长度是一个好主意(特别是在for循环的情况下),因为计算数组长度的代价很高。

 for (var i = 0; i < arr.length; i++) { } // vs for (var i = 0, arrLength = arr.length; i < arrLength; i++) { } 

但是,我想也许只有在创build和更改数组时更新length属性。 因此,阅读它不应该太昂贵的操作,而不是阅读它存储在一个variables(而不是其他语言中的其他语言,可能需要在内存中寻找的东西,例如strlen()在C )。

我有两个问题。 我也对这是如何工作感兴趣,所以请不要打我与过早的优化棒。

假设浏览器中的JavaScript引擎。

  1. 在JavaScript中caching数组的length属性有什么好处吗? 读取对象属性的局部variables是否涉及更多?
  2. length属性只是简单地改变创build和shift()pop()types的方法,不返回一个新的数组,否则简单地存储为一个整数?

那么,我会说这是昂贵的,但后来我写了一个testing@ jsperf.com和我的惊讶使用i<array.length其实在Chrome中更快,并在FF(4)没有关系。

我怀疑是长度存储为一个整数(Uint32)。 根据ECMA规范(262版5,第121页):

每个Array对象都有一个长度属性,其值始终是一个小于2 32的非负整数。 length属性的值在数值上大于名称为数组索引的每个属性的名称; 每当一个数组对象的属性被创build或更改,其他属性会根据需要进行调整以保持不变。 具体来说,无论何时添加一个名称为数组索引的属性,如果需要,length属性将被更改为比该数组索引的数值多一个; 并且每当length属性发生更改时,将自动删除名称为数组索引且其值不小于新长度的数组索引。 这个约束只适用于Array对象的属性,不受可能从原型inheritance的长度或数组索引属性的影响

唷! 我不知道我是否习惯了这种语言

最后,我们老是滞后于浏览器。 在IE(9,8,7)中,caching的速度真的很快。 我说,不使用IE的很多理由之一。

TL; DR:

从我可以收集, 看起来像arrays的长度内部caching(至less在V8)。

(详情?请阅读:))

所以,这个问题在我的脑海里萦绕了几次,我决定find问题的根源(至less在一个实现中)。

挖掘V8源代码产生了JSArray类。

 // The JSArray describes JavaScript Arrays // Such an array can be in one of two modes: // - fast, backing storage is a FixedArray and length <= elements.length(); // Please note: push and pop can be used to grow and shrink the array. // - slow, backing storage is a HashTable with numbers as keys. 

我假设数组元素的types决定了它是快还是慢。 我在set_has_fast_elementsset_bit_field2(bit_field2() | (1 << kHasFastElements)) )中设置了一个标志位,这是我想要绘制挖掘线的地方,因为我在google代码中查找,有本地来源。

现在, 似乎 任何时候在数组上完成任何操作(这是JSObject的子类,调用NormalizeElements() ,执行以下操作:

 // Compute the effective length. int length = IsJSArray() ? Smi::cast(JSArray::cast(this)->length())->value() : array->length(); 

所以,在回答你的问题时:

  1. Chrome(或使用V8的其他浏览器)在caching数组的length属性方面似乎没有任何优势(除非你做了一些奇怪的事情,会迫使它slow (我不确定那些是什么条件是) – 话虽如此,我很可能会继续cachinglength直到我有机会通过所有的操作系统浏览器实现;)
  2. 在对象的任何操作之后, length属性似乎都被改变了。

编辑:

在一个侧面说明,似乎一个“空”数组实际上分配了4个元素:

 // Number of element slots to pre-allocate for an empty array. static const int kPreallocatedArrayElements = 4; 

我不知道有多less元素数组一旦超过边界的数组增长 – 我没有挖深:)

另一套性能testing 。 循环是通过一个空循环的数百个随机数组完成的。

在Chrome中,具有caching和非caching长度的循环时钟几乎是同一时间,所以我猜测这是一个V8优化caching的长度。

在Safari和Firefox中,caching的长度始终比非caching版本快两倍。

只是一个说明:

在一些浏览器上(我在Safari,IE和Opera中已经注意到了),可以通过cachingfor循环声明中的长度来提高速度:

 var j; for (var i = 0, len = arr.length; i < len; i++) { j = arr[i]; } 

我编辑@ KooiInc的jsperftesting上面添加这种情况 。

本文将通过询问IRHydra生成的代码来研究V8和Chrome中的自动caching:

Grinch怎么偷走了Vyacheslav Egorov的array.length访问权限

他发现, 在某些情况下手动caching.length实际上增加了开销,而不是提高性能!

但无论如何,这种微观优化不太可能为您的用户带来明显的收益。 为了他们的利益,为了您的利益,请将注意力集中在清晰可读的代码上,并在您的代码中使用良好的数据结构和algorithm!

避免过早优化 :关注优雅的代码,直到出现性能问题。 只有这样,通过分析找出瓶颈,然后优化那部分代码。

注意不要认为所有迭代集合都是如此。 例如,cachingHTMLCollection的长度在Chrome(版本41)中快65%,在Firefox(版本36)中快35%。

http://jsperf.com/array-length-in-loop-dom