Javascript for..in vs for loop performance

时间:2012-11-30 13:07:18

标签: javascript

我使用kmean算法聚集了大约40000点。在程序的第一个版本中,我写了像这样的欧几里德距离函数

var euclideanDistance = function( p1, p2 ) { // p1.length === p2.length == 3
    var sum = 0;
    for( var i in p1 ){
        sum += Math.pow( p1[i] - p2[i], 2 );
    }
    return Math.sqrt( sum );
};

整个程序非常慢,平均执行时间为7秒。经过一些分析后,我重写了上面这个函数

var euclideanDistance = function( p1, p2 ) { // p1.length === p2.length == 3
    var sum = 0;
    for( var i = 0; i < p1.length; i++ ) {
        sum += Math.pow( p1[i] - p2[i], 2 );
    }
    return Math.sqrt( sum );
};

现在节目平均需要400毫秒左右。由于我编写for循环的方式,这是一个巨大的时间差异。我通常不会对数组使用for..in循环,但出于某种原因,我在编写此函数时使用了它。

有人可以解释为什么这两种风格之间存在巨大的性能差异吗?

4 个答案:

答案 0 :(得分:22)

看看每次迭代中发生的事情有何不同:

for( var i = 0; i < p1.length; i++ ) 
  1. 检查是否i < p1.length
  2. i增加一个
  3. 非常简单快捷。

    现在看看每次迭代中发生了什么:

    for( var i in p1 )

      

    重复

         
        
    1. 设P是[[Enumerable]]属性为true的obj的下一个属性的名称。如果没有这样的财产,请返回(正常,V,   空)。
    2.   

    它必须在可枚举的对象中找到下一个属性。使用你的数组,你知道这可以通过一个简单的整数增量来实现,其中找到下一个可枚举的算法很可能不那么简单,因为它必须处理任意对象及其原型链键。

答案 1 :(得分:7)

作为旁注,如果缓存p1的长度:

var plen = p1.length;
for( var i = 0; i < plen; i++ )

你的速度会略有提升。

...如果您记住该功能,它会缓存结果,因此如果用户尝试相同的数字,您将看到速度大幅提升。

var eDistance = memoize(euclideanDistance);  

function memoize( fn ) {  
    return function () {  
        var args = Array.prototype.slice.call(arguments),  
            hash = "",  
            i = args.length;  
        currentArg = null;  
        while (i--) {  
            currentArg = args[i];  
            hash += (currentArg === Object(currentArg)) ?  
            JSON.stringify(currentArg) : currentArg;  
            fn.memoize || (fn.memoize = {});  
        }  
        return (hash in fn.memoize) ? fn.memoize[hash] :  
        fn.memoize[hash] = fn.apply(this, args);  
    };  
}

eDistance([1,2,3],[1,2,3]);
eDistance([1,2,3],[1,2,3]); //Returns cached value

信用:http://addyosmani.com/blog/faster-javascript-memoization/

答案 2 :(得分:4)

首先,对于for / in和数组,您应该知道this。如果你知道你在做什么,没什么大不了的。

我运行一些非常简单的测试来显示不同循环之间的性能差异: http://jsben.ch/#/BQhED

这就是为什么更喜欢为数组使用经典for循环。

答案 3 :(得分:0)

For / In循环,只是循环遍历对象中的所有属性。由于您没有指定循环需要采用的迭代次数,因此它只是“猜测”它,并继续直到没有更多对象。

使用第二个循环,您指定所有可能的变量... a)起点,b)循环停止前应采取的迭代次数,c)增加起点的计数。

你可以这样思考...... For / In =猜测迭代次数,For(a,b,c)你指定