我发现在length
循环中缓存数组for
属性的做法非常令人反感。如,
for (var i = 0, l = myArray.length; i < l; ++i) {
// ...
}
至少在我看来,与直截了当相比,这会让人感到很伤心。
for (var i = 0; i < myArray.length; ++i) {
// ...
}
(更不用说由于词法范围和提升的性质,它会将另一个变量泄漏到周围的函数中。)
我希望能够告诉任何做这件事的人“不要打扰;现代JS JITers会优化这个技巧。”显然,这不是一个微不足道的优化,因为你可以例如在迭代过程中修改数组,但我认为考虑到所有关于JITers及其运行时分析技巧的疯狂内容,他们现在已经知道了。
任何人都有这样或那样的证据吗?
是的,我也希望说“这是微观优化就足够了;在你描述之前不要这样做。”但并不是每个人都会听到这种理由,特别是当它成为缓存长度的习惯时,他们最终会自动地这样做,几乎作为一种风格选择。
答案 0 :(得分:12)
这取决于一些事情:
从我看过的基准测试(例如,here和here)来看,IE&lt; 9(通常是你必须处理的最慢的浏览器)从缓存数组长度中受益,所以它可能值得做。对于它的价值,我有一个缓存数组长度的长期习惯,因此很容易阅读。还有其他循环优化可以起作用,例如倒计时而不是倒计时。
以下是来自JSMentors邮件列表的相关讨论:http://groups.google.com/group/jsmentors/browse_thread/thread/526c1ddeccfe90f0
答案 1 :(得分:5)
我的测试显示所有主要的新浏览器都缓存了数组的长度属性。你不需要自己缓存它,除非你担心IE6或7,我不记得确切。但是,从那时起我就一直在使用另一种迭代方式,因为它给了我另一个好处,我将在下面的例子中描述:
var arr = ["Hello", "there", "sup"];
for (var i=0, str; str = arr[i]; i++) {
// I already have the item being iterated in the loop as 'str'
alert(str);
}
如果允许数组包含'falsy'值,则必须意识到此迭代样式会停止,因此在这种情况下不能使用此样式。
答案 2 :(得分:3)
首先,如何做得更难或更不清晰?
var i = someArray.length;
while(i--){
//doStuff to someArray[i]
}
这不是一些奇怪的神秘微优化。这只是一个基本的工作规避原则。不使用'。'或者'[]'运算符超过必要应该是不重复计算pi不止一次(假设你不知道我们已经在Math对象中已经有了)。
[rantish elements yoinked]
如果someArray完全是函数的内部函数,那么它的长度属性的JIT优化是公平的游戏,它实际上就像一个getter,它实际上在每次访问它时都会计算数组的元素。 JIT可以看到它完全是本地范围的,并且跳过了实际的计数行为。
但这涉及相当大的复杂性。每次你做任何改变那个数组的事情时你必须像处理静态属性那样处理长度并告诉你的数组改变方法(我的意思是它们的本机代码方面)来手动设置属性,而通常长度只计算每次它的项目引用。这意味着每次添加新的数组更改方法时,您必须将JIT更新为分支行为,以获取本地作用域数组的长度引用。
我最终可以看到Chrome这样做,但我认为它还不是基于一些非正式的测试。我不确定IE是否会将此级别的性能微调作为优先事项。至于其他浏览器,你可以为维护问题提出一个强有力的论据,即必须为每个新的数组方法分支行为比它的价值更麻烦。至少,它不会成为首要任务。
最终,即使在典型的JS循环的旧浏览器中,每个循环周期访问length属性也不会花费你太多。但我建议养成缓存任何属性查找不止一次的习惯,因为使用getter属性,你永远无法确定做了多少工作,哪些浏览器以什么方式优化或者你可以击中什么样的性能成本当有人决定将someArray移动到函数之外时,这可能导致在十几个地方调用对象,然后在每次执行该属性访问时找到它所寻找的内容。
缓存属性查找和方法返回很容易,清理代码,并最终使其在修改时更灵活,性能更强。即使一个或两个JIT在涉及多个“ifs”的情况下确实没有必要,但您无法确定他们是否总是愿意或者您的代码将继续使其成为可能。
所以是的,为反对编译器处理它的咆哮道歉但是我不明白为什么你不想要不缓存你的属性。这很简单。它很干净。它保证了更好的性能,无论浏览器或对象的移动是否将其属性检查到外部范围。
但是真的让我感到痛苦的是,Word文档的负载现在和1995年的情况一样缓慢,并且人们继续编写极其糟糕的java网站,尽管Java的VM据称可以击败所有非编译的竞争者以获得性能。我认为这个概念可以让编译器理清性能细节,“现代计算机如此快速”与此有很大关系。当工作容易避免并且不会威胁到易读性/可维护性时,我们应该始终注意避免工作,IMO。从不同的方面做到这一点从来没有帮助我(或者我怀疑任何人)从长远来看更快地编写代码。