JavaScript:nativeforeach与nativeforeach
我注意到,即使对于小数组,native forEach有时也会显得太慢。看看这个例子: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
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实现是否如此幼稚?a和b都只由一个元素组成。如何解释这种行为?这是因为
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
测试的影响
以下是在撰写本文时指向的和实现的链接,以供参考
但是,本机的forEachforEach实现[是否]如此幼稚?a和b[均由一个元素组成]。这种行为[…]如何解释
在以下文件中有明确规定:
当使用一个或两个参数调用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是