如何更有效地做到这一点?

Ale*_*olo 4 javascript arrays optimization loops

我们假设我们应该得到一些数据......

var data = [];

//some code omitted that might fill in the data (though might not)
Run Code Online (Sandbox Code Playgroud)

然后我们需要对数据做些什么.我的问题是如何更有效地做到这一点.像这样?:

if (data.length) {
    for (var i = 0; i < data.length; i++) {
        //iterate over the data and do something to it
    }
}
Run Code Online (Sandbox Code Playgroud)

或者就是这样?:

for (var i = 0; i < data.length; i++) {
    //iterate over the data and do something to it
}
Run Code Online (Sandbox Code Playgroud)

重点是在迭代之前是否检查长度?

cry*_*ryo 8

我不认为值得检查是否for基于lengthof 来执行循环,data因为如果for循环仅执行几次,它可能在性能方面没有太大差别.

但通常,首先获得长度更快,而不是i<data.length每次都需要访问变量.至于哪种方式最有效地循环数据,不同的浏览器针对不同类型的循环进行了优化.然而,它只是IE 非常慢(在下面的测试中比其他浏览器慢几个数量级)所以我认为优化其他浏览器可能不值得.

以下是基准测试的结果(最快用+表示,最慢用 - 表示):

           FF      Chrome  Safari  Opera   IE6      IE7      IE8 
Method 1  +0.163+  0.221   0.246   0.269  -11.608- -12.214- -7.657-
Method 2   0.175  +0.133+  0.176  +0.147+   8.474    8.752   3.267
Method 3   0.206   0.235   0.276   0.245    8.002    8.539   3.651
Method 4   0.198   0.372   0.447   0.390   +6.562+  +7.020+  2.920
Method 5   0.206   0.372   0.445  -0.400-   6.626    7.096  +2.905+
Method 6   0.176   0.167  +0.175+  0.223    7.029    8.085   3.167
Method 7  -0.263- -0.567- -0.449-  0.413    6.925    7.431   3.242

方法1:使用"标准" for循环:

for (var i=0; i<data.length; i++) {
    var x = data[i]
}
Run Code Online (Sandbox Code Playgroud)

方法2:使用"标准" for循环,分配length,使其不必每次访问:

for (var i=0, len=data.length; i<len; i++) {
    var x = data[i]
}
Run Code Online (Sandbox Code Playgroud)

方法3:这类似于jQuery使用的方法$.each().请注意分配,len以便它不必length每次都获取.

for (var x=data[0], len=data.length, i=0; i<len; x=data[++i]) {}
Run Code Online (Sandbox Code Playgroud)

方法4:使用while循环,前进.警告:需要每个项目在数组中,以评估true,即不false,0,null,undefined,''等!

var x, i=0
while (x = data[i++]) {}
Run Code Online (Sandbox Code Playgroud)

方法5:与方法4相同,仅for用于执行相同操作:

for (var x,i=0; x=data[i++];) {}
Run Code Online (Sandbox Code Playgroud)

方法6:使用while以下方法向后循环循环:

var i = data.length
while (i--) {
    var x = data[i]
}
Run Code Online (Sandbox Code Playgroud)

方法7:使用方法4 /方法5,但不需要评估项目true,替换x = data[i++]:

var x, i=0, len=data.length
while ((x=data[i++]) || i<len) {}
Run Code Online (Sandbox Code Playgroud)

这首先检查是否data[i++]计算true然后检查它是否是最后一项,以便它在IE中具有类似的性能,null并且false数组中的等等问题更少.请注意,在这种情况下使用whilevs for时没有明显的差异,但我更喜欢,while因为我认为它更清楚.

我一般不喜欢优化,除非有一个特定的长期运行任务,因为它往往以可读性为代价 - 请只有在你有一个特定的情况下你需要加载大量数据时才这样做: - )

编辑:因为方法4/5在IE上如此之快,添加了一个副作用较少的版本.

编辑2:重做所有测试,这次没有任何浏览器扩展和更长的时间.这是为了完整起见的代码(抱歉这篇文章很长:)

function Tmr() {
    this.tStart = new Date()
}

Tmr.prototype = {
    Time: function() {
        var tDate = new Date()
        var tDiff = tDate.getTime() - this.tStart.getTime()
        var tDiff = tDiff / 1000.0 // Convert to seconds
        return tDiff
    }
}

function normalfor(data) {
    for (var i=0; i<data.length; i++) {
        var x = data[i]
    }
}

function fasterfor(data) {
    for (var i=0, len=data.length; i<len; i++) {
        var x = data[i]
    }
}

function jqueryfor(data) {
    for (var x=data[0], len=data.length, i=0; i<len; x=data[++i]) {

    }
}

function whileloop(data) {
    var x, i=0
    while (x = data[i++]) {

    }
}

function fixedwhileloop(data) {
    var x, i=0, len=data.length
    while ((x=data[i++]) || i<len) {

    }
}

function forwhileloop(data) {
    for (var x,i=0; x=data[i++];) {

    }
}

function fixedforwhileloop(data) {
    for (var x,i=0,len=data.length; (x=data[i++])||i<len; ) {

    }
}

function whilebackwards(data) {
    var i = data.length
    while (i--) {
        var x = data[i]
    }
}

var undefined
var NUMTIMES = 1000000
var data = '$blah blah blah blah blah|'.split('')

function test() {}
function getfntime(fn) {
    // Get the rough time required when executing one of the above functions
    // to make sure the `for` loop and function call overhead in `run` doesn't 
    // impact the benchmarks as much
    var t = new Tmr()
    for (var xx=0; xx<NUMTIMES; xx++) {
        fn()
    }
    return t.Time()
}
var fntime = getfntime(test)

function run(fn, i){
    var t = new Tmr()
    for (var xx=0; xx<NUMTIMES; xx++) {
        fn(data)
    }
    alert(i+' '+(t.Time()-fntime))
}

setTimeout('run(normalfor, "1:normalfor")', 0)
setTimeout('run(fasterfor, "2:fasterfor")', 0)
setTimeout('run(jqueryfor, "3:jqueryfor")', 0)
setTimeout('run(whileloop, "4:whileloop")', 0)
setTimeout('run(forwhileloop, "5:forwhileloop")', 0)
setTimeout('run(whilebackwards, "6:whilebackwards")', 0)
setTimeout('run(fixedwhileloop, "7:fixedwhileloop")', 0)
//setTimeout('run(fixedforwhileloop, "8:fixedforwhileloop")', 0)
Run Code Online (Sandbox Code Playgroud)