阅读数组的`length`属性真的很贵的JavaScript操作?

由于计算数组的长度非常昂贵,我总是假设在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引擎。

  • 在JavaScript中缓存数组的length属性有什么好处吗? 读取对象属性的局部变量还有更多的参与吗?
  • length属性是否仅仅在创建时以及在不返回新数组并且另存为一个整数的shift()pop()类型方法上进行了更改?

  • 那么,我会说这是昂贵的,但后来我写了一个测试@ jsperf.com,令我惊讶的是我使用i<array.length在Chrome中实际上更快,而在FF(4)中并不重要。

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

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

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

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


    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_elementsset_bit_field2(bit_field2() | (1 << kHasFastElements)) )中设置了一个标志位,这是我想要绘制挖掘线的地方,因为我正在寻找谷歌代码,而不是有本地来源。

    现在看来, 任何时候在数组上完成任何操作(这是JSObject一个子类,调用NormalizeElements() ,它将执行以下操作:

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

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

  • 在Chrome(或其他使用V8的浏览器)中缓存数组的length属性似乎没有任何优势(除非你做了一些奇怪的事情,会迫使它slow (我不确定那些是什么条件是) - 话虽如此,我很可能会继续缓存length直到我有机会浏览所有OS浏览器实现;)
  • 在对对象进行任何操作之后, length属性似乎都会改变。
  • 编辑:

    在附注中,似乎“空”数组实际被分配为具有4个元素:

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

    我不知道一旦超过界限,数组会增长多少个元素 - 我没有深入地挖掘过:)


    另一组性能测试。 循环是通过一个有空循环的百万个随机数组完成的。

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

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

    链接地址: http://www.djcxy.com/p/1881.html

    上一篇: Is reading the `length` property of an array really that expensive an operation in JavaScript?

    下一篇: hand assignment with JavaScript