首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >JavaScript:本机forEach与本机forEach

JavaScript:本机forEach与本机forEach
EN

Stack Overflow用户
提问于 2013-06-08 13:21:22
回答 3查看 2.9K关注 0票数 6

我注意到,即使对于小型数组,本机forEach有时也会出现太慢的情况。请看下面的示例:

代码语言:javascript
复制
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

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

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-06-08 13:25:10

这是因为a的长度实际上是1234568,所以你必须循环超过1234568个元素,因为你怎么能确保没有这些元素呢?

代码语言:javascript
复制
var a = []
a[1234567] = 'foo'
console.log(a.length) // 1234568

因此,它正在循环超过1234566的nothing和1 'foo',而数组b只循环在9 "nothing"s和一个'bar'上。

foreach试图读取a[0]时,它会意识到它不在那里,因为a在索引0上什么都没有。因此,foreach这样想:

代码语言:javascript
复制
I'm going to see what a[0] is!
Oh no! It's not there!
I'm going to see what a[1] is!
Oh no! It's not there!
I'm going to see what a[2] is!
Oh no! It's not there!
...
I'm going to see what a[1234567] is!
Yaaaaay! I found it! Now I'll print it!

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

票数 10
EN

Stack Overflow用户

发布于 2013-06-08 13:25:39

forEach遍历数组的全部长度,跳过不存在的元素。虽然ab只包含一个元素,但它们的length很大,因此很难迭代forEach

毕竟,它在规范中!ES5 15.4.4.18 Array.prototype.forEach节选

6)设k为0。 7)重复,而k< len

为了演示这一点,让我们看看火狐的SpiderMonkey是如何实现这些步骤的:

代码语言:javascript
复制
/* 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);
    }
}

您可以清楚地看到k0len的循环,这是性能问题的基础。对于几乎所有的kk in O都会生成false,但您仍然会感受到一百万次迭代和一百万次k in O测试的影响。

在编写本报告时,这里是指向SpiderMonkeyV8实现的链接,以供参考。

票数 5
EN

Stack Overflow用户

发布于 2013-06-08 14:02:25

但是,原生forEach实现是否如此天真呢?ab都只包含一个元素。这个行为…怎么可能要解释吗?

它是在ECMAScript语言规范,5.1版,第15.4.4.18节中指定的。

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

  1. 让O是调用ToObject的结果,传递这个值作为参数。
  2. 让lenValue是使用参数"length"调用O的[[Get]]内部方法的结果。
  3. 让连是ToUint32(lenValue).
  4. 如果IsCallable(callbackfn)false,则抛出TypeError异常。
  5. 如果提供了thisArg,则T为thisArg;否则T为未定义的
  6. 让k是0。
  7. 重复,而k< len 让Pk成为ToString(k). 让kPresent是调用带有参数Pk的O的[[HasProperty]]内部方法的结果。 如果kPresent是,那么 让kValue是调用带有参数Pk的O的[[Get]]内部方法的结果。 二、调用[[Call]]的回调函数的内部方法,以T作为--值和包含kValue、k和O的参数列表。

d.将k增加1。

  1. 返回未定义的

步骤6和步骤7需要一个一致的实现来迭代所有索引,从第一个索引0到最后一个索引a.length - 1,而不管是否有该索引的数组元素。这解释了为什么如果最后一个索引是一个大数,那么forEach方法调用会花费这么长的时间。

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

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/16999929

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档