For循环性能:将数组长度存储在变量中

duc*_*cin 50 javascript performance for-loop

考虑同一循环迭代的两个版本:

for (var i = 0; i < nodes.length; i++) {
    ...
}
Run Code Online (Sandbox Code Playgroud)

var len = nodes.length;
for (var i = 0; i < len; i++) {
    ...
}
Run Code Online (Sandbox Code Playgroud)

后者的版本无论如何比前者快吗?

Esa*_*ija 34

接受的答案是不正确的,因为任何体面的引擎都应该能够通过如此简单的环体将环境中的属性负载提升出来.

看到这个jsperf - 至少在V8 中有趣的是看看如何将它实际存储在变量中更改寄存器分配 - 在使用sum变量的代码中,变量存储在堆栈中而使用array.length-in-a-loop-code它存储在寄存器中.我假设SpiderMonkey和Opera也发生了类似的事情.

根据作者的说法,JSPerf使用不正确,占70%的时间.这里所有答案中给出的这些破碎的jsperf给出了误导性的结果,人们从中得出了错误的结论.

一些危险信号将代码放在测试用例中而不是函数中,不测试结果的正确性或使用某种消除死代码消除的机制,在设置或测试用例中定义函数而不是全局..为了保持一致性,你需要热情 - 在任何基准测试之前都要测试测试函数,以便在定时部分中不会发生编译.

  • 很多人忘记了编译器的用途,并且没有必要为了纳米收益而对代码进行游戏,从长远来看甚至可能是有害的。 (2认同)

Nei*_*ord 27

更新:2015年12月16日

由于这个答案似乎仍然有很多观点,我想重新审视这个问题,因为浏览器和JS引擎不断发展.

我没有使用JSPerf,而是使用原始问题中提到的两种方法将一些代码放在一起来循环遍历数组.我已经将代码放入函数中来分解功能,希望在现实世界的应用程序中完成:

function getTestArray(numEntries) {
    var testArray = [];
    for(var i = 0; i < numEntries; i++) {
        testArray.push(Math.random());
    }
    return testArray;
}

function testInVariable(testArray) {
    for (var i = 0; i < testArray.length; i++) {
        doSomethingAwesome(testArray[i]);
    }
}

function testInLoop(testArray) {
    var len = testArray.length;
    for (var i = 0; i < len; i++) {
        doSomethingAwesome(testArray[i]);
    }
}

function doSomethingAwesome(i) {
    return i + 2;
}

function runAndAverageTest(testToRun, testArray, numTimesToRun) {
    var totalTime = 0;
    for(var i = 0; i < numTimesToRun; i++) {
        var start = new Date();
        testToRun(testArray);
        var end = new Date();
        totalTime += (end - start);
    }
    return totalTime / numTimesToRun;
}

function runTests() {
    var smallTestArray = getTestArray(10000);
    var largeTestArray = getTestArray(10000000);

    var smallTestInLoop = runAndAverageTest(testInLoop, smallTestArray, 5);
    var largeTestInLoop = runAndAverageTest(testInLoop, largeTestArray, 5);
    var smallTestVariable = runAndAverageTest(testInVariable, smallTestArray, 5);
    var largeTestVariable = runAndAverageTest(testInVariable, largeTestArray, 5);

    console.log("Length in for statement (small array): " + smallTestInLoop + "ms");
    console.log("Length in for statement (large array): " + largeTestInLoop + "ms");
    console.log("Length in variable (small array): " + smallTestVariable + "ms");
    console.log("Length in variable (large array): " + largeTestVariable + "ms");
}

runTests();
runTests();
runTests();
Run Code Online (Sandbox Code Playgroud)

为了尽可能公平地进行测试,每次测试运行5次,结果取平均值.我还运行了整个测试,包括3次生成数组.在我的机器上对Chrome进行测试表明,使用每种方法花费的时间几乎相同.

重要的是要记住,这个示例是一个玩具示例,事实上,从应用程序上下文中取出的大多数示例都可能产生不可靠的信息,因为您的代码正在执行的其他操作可能直接或间接地影响性能.

底线

确定最适合您的应用程序的最佳方法是自己测试!JS引擎,浏览器技术和CPU技术不断发展,因此您必须始终在应用程序环境中自行测试性能.同样值得问问自己是否存在性能问题,如果不这样做,那么花费时间进行用户难以察觉的微优化可以更好地修复错误和添加功能,从而使用户更快乐:).

原答案:

后者会略快一些.该length属性不会遍历数组以检查元素的数量,但每次在数组上调用时,都必须取消引用该数组.通过将长度存储在变量中,每次循环迭代都不需要数组解除引用.

如果你对在javascript中循环遍历数组的不同方式的性能感兴趣,那么看看这个jsperf

  • `length`是属性,而不是函数. (8认同)
  • 你的回答是最准确的。我认为差异并不显着,尤其是考虑到铬。 (3认同)