JavaScript:nativeforeach与nativeforeach

JavaScript:nativeforeach与nativeforeach,javascript,performance,foreach,Javascript,Performance,Foreach,我注意到,即使对于小数组,native forEach有时也会显得太慢。看看这个例子: var a = [], b = []; a[1234567] = 'foo'; b[10] = 'bar'; a.forEach(function(arg1, arg2) { console.log(arg1, arg2); }); //1 //vs b.forEach(function(arg1, arg2) { console.log(arg1, arg2); }); //2 在我的Chrom

我注意到,即使对于小数组,native forEach有时也会显得太慢。看看这个例子:

var a = [], b = []; 
a[1234567] = 'foo'; 
b[10] = 'bar'; 

a.forEach(function(arg1, arg2) { console.log(arg1, arg2); }); //1
//vs 
b.forEach(function(arg1, arg2) { console.log(arg1, arg2); }); //2

在我的Chromium(25.0.1364.160 Ubuntu 12.04)中,第1行和第2行的执行时间是不同的数量级。我知道a的长度等于1234568,而b的长度仅等于10。但原生forEach实现是否如此幼稚?ab都只由一个元素组成。如何解释这种行为?

这是因为
a
的长度实际上是1234568,所以必须循环1234568个元素,因为还有什么方法可以确保元素不在那里

var a = []
a[1234567] = 'foo'
console.log(a.length) // 1234568
因此,它在1234566个空和1个
'foo'
上循环,而数组
b
只在9个空和一个
'bar'
上循环

foreach
尝试读取
a[0]
时,它意识到它不在那里,因为
a
在索引
0
中没有任何内容。因此,
foreach
的想法如下:

我要看看[0]是什么!
哦,不!它不在那里!
我要看看a[1]是什么!
哦,不!它不在那里!
我要看看a[2]是什么!
哦,不!它不在那里!
...
我要看看[1234567]是什么!
哎呀!我找到了!现在我要把它打印出来!

这就是为什么要花这么长的时间。

forEach
在数组的整个长度上迭代,同时跳过不存在的元素。虽然
a
b
只包含一个元素,但它们的
长度很大,因此给
forEach
带来了一段艰难的迭代时间

毕竟,它在规范中!摘自:

6) 设k为0

7) 当k 为了演示这一点,让我们看看Firefox的SpiderMonkey如何实现这些步骤:

/* Steps 6-7. */
/* Steps a (implicit), and d. */
for (var k = 0; k < len; k++) {
    /* Step b */
    if (k in O) {
        /* Step c. */
        callFunction(callbackfn, T, O[k], k, O);
    }
}
/*步骤6-7*/
/*步骤a(隐式)和d*/
对于(var k=0;k
您可以清楚地看到
k
0
len
的循环,这是性能问题的基础。对于几乎所有的
k
k in O
都会产生
false
,但您仍然会感受到一百万次迭代和一百万次
k in O
测试的影响

以下是在撰写本文时指向的和实现的链接,以供参考

但是,本机的forEach
forEach实现[是否]如此幼稚?ab[均由一个元素组成]。这种行为[…]如何解释

在以下文件中有明确规定:

当使用一个或两个参数调用forEach
方法时,将执行以下步骤:

  • 让O作为调用的结果,将this值作为参数传递
  • 让lenValue作为使用参数
    “length”
    调用O的
    [[Get]]
    内部方法的结果
  • 设len为
    lenValue
  • 如果
    callbackfn
    false,则抛出
    TypeError
    异常
  • 如果提供了thisArg,则T为thisArg;否则让T未定义
  • 设k为0
  • 当k a。设Pk为
    k

    b。让kPresent作为调用带有参数Pk的O的
    [[HasProperty]]
    内部方法的结果

    c。如果kPresent为true,则

    一,。让kValue作为使用参数Pk调用O的
    [[Get]]
    内部方法的结果

    二,。调用callbackfn的
    [[Call]]
    内部方法,T作为这个值和包含kValue、k和O的参数列表

    d。把k增加1

  • 返回未定义的
  • 第6步和第7步要求一致性实现迭代所有索引,从第一个索引
    0
    到最后一个索引
    a.length-1
    ,而不管是否存在具有该索引的数组元素。这就解释了为什么如果最后一个索引是一个大数字,那么调用
    forEach
    方法会花费很长时间


    但是,由于在步骤7b(实现)中,
    [[HasProperty]]
    内部方法必须为没有数组元素的索引返回false,因此不会为这些索引调用回调callbackfn。这解释了为什么在执行
    forEach
    方法调用时只有一个
    console.log
    调用。

    如果a只有一个元素,它的长度将是1。
    b[10]=
    是否会自动为
    b[9]
    b[8]
    等创建值?根据,迭代方法在开始之前对数组的长度进行采样。因此,forEach将迭代a的每个可能索引,即使该数组中只定义了一个元素。@jpaugh:没有,它只是变成了一个稀疏数组。错误,正如您在
    hasOwnProperty()中看到的
    for…请详细说明?答案的哪一部分是错误的?数组不包含
    未定义的
    元素。读取该属性只会产生未定义的
    ,就像其他任何不存在的属性一样。让我们@PointedEars不,你在胡说八道<代码>对于(a中的var i)
    不等同于
    a.forEach
    @布鲁诺:不,看报纸。没有未定义的
    元素。索引中没有元素,因此没有此类属性,使用不存在的属性的名称会产生
    未定义的
    @PointedEars,如果您进一步阅读(forEach部分),它确实从“k为0”开始,并在“len”结束(其中len是