Javascript for..in vs for循环性能

Javascript for..in vs for循环性能,javascript,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 )

我使用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 );
};
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 );
};
整个程序相当慢,平均执行时间为7秒。经过一些分析之后,我像这样重写了上面的函数

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 );
};
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 );
};
var-euclideanDistance=函数(p1,p2){//p1.length==p2.length==3
var总和=0;
对于(变量i=0;i
现在这些程序平均需要400毫秒左右。这是一个巨大的时间差,因为我写for循环的方式。我通常不在数组的循环中使用
for..in,但出于某种原因,我在编写此函数时使用了它


有人能解释一下为什么这两种样式在性能上有如此巨大的差异吗?

For/in循环只是循环一个对象中的所有属性。由于您没有指定循环需要进行的迭代次数,因此它只是“猜测”循环次数,并继续进行,直到没有更多对象为止

对于第二个循环,您正在指定所有可能的变量。。。a) a起点,b)循环停止前应进行的迭代次数,c)增加起点的计数


你可以这样想。。。For/In=猜测迭代次数,对于(a、b、c)您指定的

查看每个迭代中发生的不同情况:

for( var i = 0; i < p1.length; i++ ) 
for(变量i=0;i
  • 检查
    i
  • i
    增加1
  • 非常简单和快速

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

    重复

  • 设P为obj的下一个属性的名称,其[[Enumerable]]属性为true。如果没有这样的属性,返回(正常,V, 空的)

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

    首先,您应该注意for/in和数组的情况。如果你知道自己在做什么,那没什么大不了的

    我运行了一些非常简单的测试,以显示不同循环之间的性能差异:


    这就是为什么您更喜欢使用classic for loop for Array。

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

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

    信贷:

    请注意,在数组上使用
    for..in
    循环。
    for..in
    枚举对象键,for循环增加整数并检查简单条件。。不明显吗?p1和p2看起来像密集的值数组。我怀疑解释器正是在优化这个案例。还可以尝试将p1.length的查询(您知道它不会改变,但解释器不能假设)从循环中拉出来-应该会有更大的改进。这里重复的答案是解释性的:@DeletedComment,是的,只有规范中的第一行才是真正相关的。我想说的是,运行在对象及其原型链中查找下一个可枚举键的复杂算法的性能远不如整数加法和布尔检查。这是模糊的,不确定这里发生了什么。因此,for/in循环已经“知道”了它需要进行的迭代次数?(对不起,我不会说技术性的……我花了太多时间与客户交谈,并为他们大声疾呼)是的,是的。在内部,有一个存储对象所有属性的属性列表,并且在Mozilla的实现中,对于可枚举属性的数量,有一个
    \uuuuuuu count\uuuuuuu
    属性。:)在FF和Chrome for循环中,无需缓存即可正常工作。然而,歌剧是不同的故事。在该浏览器中,没有缓存的版本速度慢2倍…为什么条件对象初始化发生在
    while
    循环中?