在 JavaScript 中读取数组的 `length` 属性真的那么昂贵吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5752906/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-25 18:12:42  来源:igfitidea点击:

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

javascriptarrays

提问by alex

I always assumed caching the length of an array in JavaScript is a good idea (especially in the condition of a forloop) because of the expensiveness of calculating the length of an array.

我一直认为在 JavaScript 中缓存数组的长度是一个好主意(尤其是在for循环条件下),因为计算数组的长度很昂贵。

Example

例子

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

// vs

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

However, I thought perhaps the lengthproperty is only updated on creation and alteration of the array. Therefore, reading it shouldn't be too expensive an operation as opposed to reading it stored in a variable (as opposed to other methods in other languages that may need to seek in memory to find the end of something, e.g. strlen()in C).

但是,我认为该length属性可能仅在创建和更改数组时更新。因此,读取它不应该是一个太昂贵的操作,而不是读取它存储在变量中(与其他语言中的其他方法可能需要在内存中寻找以找到某些东西的结尾相反,例如strlen()在 C 中)。

I have two questions. I am also interested in how this works, so please don't hit me with the premature optimisationstick.

我有两个问题。我也对它的工作原理很感兴趣,所以请不要用过早的优化棒打击我。

Assume the JavaScript engines in browsers.

假设浏览器中的 JavaScript 引擎。

  1. Is there any advantage to caching the lengthproperty of an array in JavaScript? Is there much more involved in reading a local variable over an object's property?
  2. Is the lengthproperty simply altered on creation and on shift()and pop()type methods that don't return a new array and otherwise simply stored as an integer?
  1. length在 JavaScript 中缓存数组的属性有什么好处吗?通过对象的属性读取局部变量是否涉及更多内容?
  2. length属性只是改变对创造和shift()pop()类型的方法不返回一个新的数组否则简单地存储为整数?

采纳答案by KooiInc

Well, I would have said it was expensive, but then I wrote a little test @ jsperf.comand to my surprise using i<array.lengthactually was faster in Chrome, and in FF(4) it didn't matter.

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

My suspicion is that length is stored as an integer (Uint32). From the ECMA-specs (262 ed. 5, page 121):

我怀疑长度存储为整数(Uint32)。来自 ECMA 规范(第 262 版,第 5 版,第 121 页):

Every Array object has a length property whose value is always a nonnegative integer less than 232. The value of the length property is numerically greater than the name of every property whose name is an array index; whenever a property of an Array object is created or changed, other properties are adjusted as necessary to maintain this invariant. Specifically, whenever a property is added whose name is an array index, the length property is changed, if necessary, to be one more than the numeric value of that array index; and whenever the length property is changed, every property whose name is an array index whose value is not smaller than the new length is automatically deleted. This constraint applies only to own properties of an Array object and is unaffected by length or array index properties that may be inherited from its prototypes

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

Phew! I don't know if I ever get used to such language ...

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

Finally, we always have our good old lagging behind browser. In IE (9, 8, 7) caching the length is really faster. One of many more reasons to not use IE, I say.

最后,我们总是落后于浏览器。在 IE (9, 8, 7) 中,缓存长度确实更快。我说,这是不使用 IE 的更多原因之一。

回答by Demian Brecht

TL;DR:

特尔;博士:

From what I can gather, it seemslike the length of the array is cached internally (at least in V8)..

据我所知,数组的长度似乎是在内部缓存的(至少在 V8 中)。

(Details? Read on :))

(详细信息?继续阅读:))

So, this question has dinged around in my head a few times and I've decided to get to the root of the problem (at least, in one implementation).

所以,这个问题在我的脑海中出现了几次,我决定找到问题的根源(至少,在一个实现中)。

Digging around V8 source yielded the JSArrayclass.

挖掘 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.

I'm making an assumption that the type of array elements dictates whether it's fast or slow. I got down to a bit flag being set in set_has_fast_elements(set_bit_field2(bit_field2() | (1 << kHasFastElements))), which is where I figured I'd draw the digging line as I was looking in google code and don't have the source locally.

我假设数组元素的类型决定了它是快还是慢。我在set_has_fast_elements( set_bit_field2(bit_field2() | (1 << kHasFastElements)))中设置了一个位标志,这是我想在我查看 google 代码时绘制挖掘线的地方,并且在本地没有源代码。

Now, it seemsthat anytime anyoperation is done on the array (which is a child class of JSObject, a call is made to NormalizeElements(), which executes the following:

现在,它似乎任何时候任何操作是在阵列上完成(这是一个子类的JSObject,就会调用作出NormalizeElements(),其执行以下操作:

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

So, in answering your questions:

因此,在回答您的问题时:

  1. There doesn't seem to be any advantage in Chrome (or other browsers that use V8) to caching the lengthproperty of an array (unless you're doing something odd that would force it to be slow(I'm not sure what those conditions are) - having said that, I'll most likely continue to cache lengthuntil I get a chance to go through allOS browser implementations ;)
  2. The lengthproperty seems to be altered after anyoperation on the object.
  1. Chrome(或其他使用 V8 的浏览器)似乎没有任何优势来缓存length数组的属性(除非你做一些奇怪的事情会迫使它成为slow(我不确定这些条件是什么) ) - 话虽如此,我很可能会继续缓存,length直到我有机会完成所有操作系统浏览器实现;)
  2. 在对对象length进行任何操作后,该属性似乎已更改。

Edit:

编辑:

On a side note, it seems that an "empty" array is actually allocated to have 4 elements:

附带说明一下,似乎“空”数组实际上被分配为具有 4 个元素:

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

I'm not sure how many elements the array grows by once the bounds have been exceeded - I didn't dig thatdeep :)

我不知道数组有多少元素增长由曾经的范围已经超出了-我没有挖的是深:)

回答by Anurag

Another set of performance tests. The loop is done over an array of million random numbers with an empty loop.

另一组性能测试。循环是在一个包含空循环的百万随机数数组上完成的。

In Chrome, the loops with cached and non-cached lengths clock pretty much the same times, so I am guessing it's a V8optimization to cache the length.

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

In Safari and Firefox, the cached length was consistently about 2x faster than the non-cached version.

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

回答by joeytwiddle

This article investigates automatic caching in V8 and Chrome, by asking IRHydrafor the generated code:

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

How the Grinch stole array.length accessby Vyacheslav Egorov

Grinch 如何窃取Vyacheslav Egorov 的array.length 访问权限

He found that under certain conditionsmanually caching the .lengthactually added overhead rather than improving performance!

他发现在某些情况下手动缓存.length实际增加的开销而不是提高性能!

But anyway, this kind of micro-optimization is unlikely to achieve any noticeable gain for your users. For their benefit, and for yours, focus instead on code that is clear to read, and using good data structures and algorithms in your code!

但无论如何,这种微优化不太可能为您的用户带来任何显着的收益。为了他们和您的利益,请专注于易于阅读的代码,并在您的代码中使用良好的数据结构和算法!

Avoid premature optimisation: Focus on elegant code until a performance issue arises. Only then, seek out the bottleneck through profiling, and then optimise just that partof the code.

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

回答by musicnothing

Just a note:

只是一个注意事项:

On some browsers (I've noticed it in Safari, IE, and Opera), you can get a speed boost by caching the length inside the for loop declaration:

在某些浏览器上(我在 Safari、IE 和 Opera 中注意到了这一点),您可以通过在 for 循环声明中缓存长度来提高速度:

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

I edited @KooiInc's jsperf test above to add this case.

我在上面编辑了@KooiInc 的 jsperf 测试以添加这种情况

回答by Dustin Hayes

Take care not to assume that this is true for all iterable collections. For example caching the length of an HTMLCollection is 65% faster in Chrome (Version 41) and 35% faster in Firefox (Version 36).

注意不要假设所有可迭代集合都是如此。例如,缓存 HTMLCollection 的长度在 Chrome(版本 41)中快 65%,在 Firefox(版本 36)中快 35%。

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

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