缓存 Array.length
是老生常谈的小优化。javascript
// 不缓存 for (var i = 0; i < arr.length; i++) { ... } // 缓存 var len = arr.length; for (var i = 0; i < len; i++) { ... } // 或者 for (var i = 0, len = arr.length; i < len; i++) { ... }
但之前写过 Java 的笔者一直对这种破碎的写法感到不适,也对这种写法的实际优化效果产生疑问。html
且推崇这种写法的朋友彷佛不少也是“前辈这么说+本身想了一下以为有道理”。java
因为 for 循环搭配 Array.length
是极度经常使用的 JavasScript 代码,因此仍是有必要搞清楚的。数组
通过一番摸索后笔者获得的结论是:缓存 Array.lengh
对优化的影响没有想象中的大,甚至可能会有所减慢。缓存
stackoverflow 上也有这个讨论,For-loop performance: storing array length in a variable 。jsp
accepted 的答案是说缓存会起到加速的结果,给出了 jsPerf 测试。函数
可是有答案反对,也给出了 jsPerf 测试。oop
两个答案的区别在于“循环不变量代码移动(Loop-invariant code motion)”,accepted 答案的测试循环里没有访问到数组,是不实际的,后面会讲到。测试
从另外一篇文章 Shoud I have to cache my array’s length? 的测试结果也能够看出缓存差异不大。优化
还有这篇 JavaScript's .length Property is a Stored Value
这篇文章 How the Grinch stole array.length access 从 V8 的 hydrogen 探讨 Array.length
在 for 循环中的处理。
正如上面提到的“循环不变量代码移动”,V8 引擎会聪明的把能肯定不变的代码移到循环外。
因此像下面这种代码也不会影响引擎对 Array.length
的优化:
function uncached(arr) { for (var i = 0; i < arr.length; i++) { arr[i] } }
而当在循环中调用函数时,V8 会尝试将函数进行内联,从而继续进行“循环不变量代码移动”优化。
但当函数不可内联时,V8 就没辙了,每次循环都要从新计算一遍 length
function BLACKHOLE(sum, arr) { try { } catch (e) { } } function uncached(arr) { var sum = 0; for (var i = 0; i < arr.length; i++) { sum += arr[i]; if (sum < 0) BLACKHOLE(arr, sum); } return sum; }
但这时即使是在循环外缓存了 length
也是没有用的,引擎无法预判数组的变化,当须要访问数组元素时会触发 bounds check ,从而照样要计算一遍 length
。因此缓存 length
是没有用的。
甚至,因为多了一个变量,底层的寄存器分配器每次循环还要多一次恢复这个变量。固然这个只有在大规模的状况下才会看出区别。
固然这篇文章也有局限性,仅仅讨论了 V8 引擎,也没有讨论访问 length
代价更高的 HTMLCollection
。但这已经足够说明二者差异不大,通常状况下咱们不用再局限于缓存的写法,能够放开来按照本身喜欢的方式去写循环了。
【完】
原文:http://div.io/topic/966