读取一个数组的`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引擎。
- 在JavaScript中caching数组的
length
属性有什么好处吗? 读取对象属性的局部variables是否涉及更多? - 是
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_elements
( set_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();
所以,在回答你的问题时:
- Chrome(或使用V8的其他浏览器)在caching数组的
length
属性方面似乎没有任何优势(除非你做了一些奇怪的事情,会迫使它slow
(我不确定那些是什么条件是) – 话虽如此,我很可能会继续cachinglength
直到我有机会通过所有的操作系统浏览器实现;) - 在对象的任何操作之后,
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%。