我总是假设在JavaScript中缓存数组的长度是一个好主意(特别是在for
循环的条件下),因为计算数组长度的代价很高。
for (var i = 0; i < arr.length; i++) { }
// vs
for (var i = 0, arrLength = arr.length; i < arrLength; i++) { }
但是,我认为length
属性可能仅在创建和更改数组时更新。因此,阅读它不应该是一个操作太昂贵而不是读取存储在变量中的操作(与其他语言中可能需要在内存中寻找某些内容的其他方法相反,例如strlen()
在C)。
我有两个问题。我也对它是如何工作感兴趣,所以请不要用过早优化棒来打击我。
假设浏览器中的JavaScript引擎。
length
属性有什么好处吗?是否有更多的参与在对象的属性上读取局部变量?length
属性是否仅在创建时以及shift()
和pop()
类型方法上更改,这些方法不返回新数组,否则只是存储为整数?答案 0 :(得分:47)
好吧,我会说它很贵,但后来我写了一个小测试@ jsperf.com,令我惊讶的是使用i<array.length
实际上在Chrome中更快,在FF(4)它没有没关系
我怀疑是长度存储为整数(Uint32)。来自ECMA-specs(262 ed.5,page 121):
每个Array对象都有一个 length属性,其值始终是小于2 32 的非负整数。 length属性的值是 在数字上大于名称 名称为数组的每个属性 指数;每当一个数组的属性 对象是创建或更改的,其他 根据需要调整属性 保持这种不变性。 具体来说,无论何时财产 添加了谁的名字是一个数组索引, 如果,则更改length属性 必要的,比一个更多 该数组索引的数值;和 每当长度属性是 改变了,每个属性的名字都是 一个数组索引,其值不是 小于新的长度 自动删除。这个约束 仅适用于自己的属性 数组对象并且不受影响 长度或数组索引属性 可以从其原型继承
唷!我不知道我是否习惯了这种语言......
最后,我们总是让我们的老人落后于浏览器。在IE(9,8,7)中,缓存的长度确实更快。我说,不使用IE的更多原因之一。
答案 1 :(得分:26)
TL; DR:
从我可以收集到的内容来看,似乎就像内部缓存数组的长度一样(至少在V8中)..
(详情?继续阅读:))
所以,这个问题已经在我的脑海中爆发了几次,我决定找到问题的根源(至少在一个实现中)。
挖掘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.
我假设数组元素的类型决定它是快还是慢。我在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();
所以,在回答你的问题时:
length
属性(除非你做了一些奇怪的事情会强制它slow
1}}(我不确定那些条件是什么) - 说过,我很可能会继续缓存length
,直到我有机会通过所有操作系统浏览器实现;)length
属性似乎已被更改。修改强>
另外,似乎“空”数组实际上分配了4个元素:
// Number of element slots to pre-allocate for an empty array.
static const int kPreallocatedArrayElements = 4;
我不确定一旦超出界限,数组会增加多少元素 - 我没有深入挖掘
答案 2 :(得分:12)
另一组效果tests。循环在一百万个随机数的数组上完成,并带有一个空循环。
在Chrome中,缓存和非缓存长度的循环时间几乎相同,因此我猜测缓存长度是V8优化。
在Safari和Firefox中,缓存长度始终比非缓存版本快2倍。
答案 3 :(得分:5)
本文通过询问IRHydra生成的代码来调查V8和Chrome中的自动缓存:
Vyacheslav Egorov
他发现在某些条件下手动缓存.length
实际增加的开销而不是提高性能!
但无论如何,这种微优化不太可能为您的用户带来任何显着的收益。为了他们的利益,为您的利益而重点关注明确阅读的代码,并在代码中使用良好的数据结构和算法!
Avoid premature optimisation:专注于优雅的代码,直到出现性能问题。只有这样,通过分析找出瓶颈,然后优化代码的那部分。
答案 4 :(得分:4)
请注意:
在某些浏览器上(我在Safari,IE和Opera中注意到它),您可以通过缓存for循环声明中的长度来提高速度:
var j;
for (var i = 0, len = arr.length; i < len; i++) {
j = arr[i];
}
我在上面编辑了@ KooiInc的jsperf测试add this case。
答案 5 :(得分:2)
注意不要假设所有可迭代集合都是如此。例如,缓存HTMLCollection的长度在Chrome(版本41)中快65%,在Firefox(版本36)中快35%。