【问题标题】:JavaScript for...in vs forJavaScript for...in 与 for
【发布时间】:2010-09-19 13:28:17
【问题描述】:

你认为 for...in 和 for 循环有很大的不同吗?您更喜欢使用哪种“for”,为什么?

假设我们有一个关联数组:

var myArray = [{'key': 'value'}, {'key': 'value1'}];

所以我们可以迭代:

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

还有:

for (var i in myArray)

我看不出有什么大的不同。是否存在性能问题?

【问题讨论】:

  • 请注意,as of JS 1.6 也有:myArray.forEach(callback[, thisarg])
  • @Benji array.forEach 实际上在 ES5 中。
  • 在 for-in 循环中,您需要一个如下所示的条件:if(myArray.hasOwnProperty(i)){true}
  • ['foo', 'bar', 'baz'].forEach(function(element, index, array){ console.log(element, index, array); }); 几乎可以在任何地方使用,除了在 IE8 中,它是迄今为止最优雅的语法
  • ECMAScript 6中也有for...of声明,例如:for (let i of myArray) console.log(i);

标签: javascript


【解决方案1】:

虽然它们都非常相似,但有一个细微的差别:

var array = ["a", "b", "c"];
array["abc"] = 123;
console.log("Standard for loop:");
for (var index = 0; index < array.length; index++)
{
  console.log(" array[" + index + "] = " + array[index]); //Standard for loop
}

在这种情况下,输出是:

循环标准:

阵列[0] = A

数组[1] = B

数组[2] = C

console.log("For-in loop:");
for (var key in array)
{
  console.log(" array[" + key + "] = " + array[key]); //For-in loop output
}

而在这种情况下,输出是:

FOR-IN 循环:

数组[1] = B

数组[2] = C

数组[10] = D

数组[ABC] = 123

【讨论】:

    【解决方案2】:

    Douglas Crockford 在 JavaScript: The Good Parts(第 24 页)中建议避免使用 for in 语句。

    如果您使用for in 循环对象中的属性名称,则结果不会排序。更糟糕的是:你可能会得到意想不到的结果;它包括从原型链继承的成员和方法的名称。

    除了属性之外的所有内容都可以使用.hasOwnProperty 过滤掉。此代码示例执行您最初可能想要的操作:

    for (var name in obj) {
        if (Object.prototype.hasOwnProperty.call(obj, name)) {
            // DO STUFF
        }
    }
    

    【讨论】:

    • for...in 非常适合循环对象属性。它不适用于遍历数组元素。如果您无法理解这些场景之间的区别,那么是的,您应该避免 for...in;否则,发疯。
    • 想要强调的是它不是订购的!这可能是个大问题,而且很难捕捉到错误。
    • +1 表示“它包括从原型链继承的成员和方法的名称。”例如,如果有人碰巧在加载了 Prototype 的情况下使用您的代码(即使您的代码实际上并未使用它),您会很开心。
    • 请不要忘记声明 name 变量:for(var name in object)...,否则,如果该代码在函数内部,例如,name 变量 end up 是全局对象的一个​​属性(对 未声明的标识符 的赋值),同样在新的 ECMAScript 5 严格模式下,该代码将抛出 ReferenceError
    • @Nosredna:有一个关于 Chrome 迭代顺序的问题,由 John Resig 提交,标记为 WontFix。 code.google.com/p/chromium/issues/detail?id=883 。即使在 chrome 之前,如果您删除然后再次添加属性,则跨浏览器的迭代顺序也不相同。 IE 9 的行为也很像 chrome(据说是为了提高速度)。所以...请停止传播不准确的信息,继续依赖它会很天真。
    【解决方案3】:

    两者之间有一个重要的区别。 for-in 迭代对象的属性,因此当 case 是一个数组时,它不仅会迭代其元素,还会迭代它具有的“删除”函数。

    for (var i = 0; i < myArray.length; i++) { 
        console.log(i) 
    }
    
    //Output
    0
    1
    
    for (var i in myArray) { 
        console.log(i) 
    } 
    
    // Output
    0 
    1 
    remove
    

    您可以将 for-in 与 if(myArray.hasOwnProperty(i)) 一起使用。尽管如此,在遍历数组时,我总是更喜欢避免这种情况,而只使用 for(;;) 语句。

    【讨论】:

      【解决方案4】:

      for(;;) 适用于 数组:[20,55,33]

      for..in 用于对象:{x:20,y:55:z:33}

      【讨论】:

        【解决方案5】:

        根据 jsperf 的更短和最好的代码是

        keys  = Object.keys(obj);
        for (var i = keys.length; i--;){
           value = obj[keys[i]];// or other action
        }
        

        【讨论】:

          【解决方案6】:

          2012 年更新答案所有主流浏览器的当前版本 - Chrome、Firefox、IE9、Safari 和 Opera 支持 ES5 的原生 array.forEach。

          除非您有某些理由原生支持 IE8(请记住,可以为这些用户提供 ES5-shim 或 Chrome 框架,这将提供适当的 JS 环境),否则只需使用该语言的适当语法就更简洁了:

          myArray.forEach(function(item, index) {
              console.log(item, index);
          });
          

          Full documentation for array.forEach() is at MDN.

          【讨论】:

          • 你应该真正记录回调的参数:第一个元素值,第二个元素索引,第三个被遍历的数组
          • 我听到你在说什么,但在这种情况下,过度简化它会掩盖所有可能性。同时拥有索引和值意味着它可以作为 for...in 和 for each...in 的替代品——好处是您不必记住哪个迭代键或值。
          • @Cory:ES5 forEach 可以很容易地添加到旧版 ES3 浏览器中。更少的代码就是更好的代码。
          • @nailer 可以在数组和对象上互换使用吗?
          • @hitautodestruct 它是 Array 原型的一部分,而不是 Object 的。目前在社区中,非数组对象通常仍使用“for (var key in object){}”进行迭代。
          【解决方案7】:

          小心!!! 我在 Mac OS 中使用 Chrome 22.0,但对于每种语法都有问题。

          我不知道这是浏览器问题、javascript 问题还是代码中的一些错误,但这很奇怪。在对象之外它可以完美地工作。

          var MyTest = {
              a:string = "a",
              b:string = "b"
          };
          
          myfunction = function(dicts) {
              for (var dict in dicts) {
                  alert(dict);
                  alert(typeof dict); // print 'string' (incorrect)
              }
          
              for (var i = 0; i < dicts.length; i++) {
                  alert(dicts[i]);
                  alert(typeof dicts[i]); // print 'object' (correct, it must be {abc: "xyz"})
              }
          };
          
          MyObj = function() {
              this.aaa = function() {
                  myfunction([MyTest]);
              };
          };
          new MyObj().aaa(); // This does not work
          
          myfunction([MyTest]); // This works
          

          【讨论】:

            【解决方案8】:

            我的第二意见是你应该根据自己的需要选择迭代方法。我建议您实际上不要永远循环使用 for in 结构的原生 Array。正如 Chase Seibert 刚才指出的那样,它的速度要慢得多并且,与 Prototype 框架不兼容。

            有一个很好的benchmark on different looping styles that you absolutely should take a look at if you work with JavaScript。不要做早期的优化,但你应该把这些东西放在脑后的某个地方。

            我会使用for in 来获取对象的所有属性,这在调试脚本时特别有用。例如,当我探索不熟悉的对象时,我喜欢使用这条线:

            l = ''; for (m in obj) { l += m + ' => ' + obj[m] + '\n' } console.log(l);
            

            它将整个对象的内容(连同方法主体)转储到我的 Firebug 日志中。 非常方便。

            【讨论】:

            • 链接现已断开。如果有人有另一个链接,当然想查看基准。
            • Foreach 循环在原型中中断?由于它现在得到普遍支持,因此原型应该解决这个问题。
            • 答案中的链接已过时 - “404 | 未找到 | 您正在查看的内容已不在此页面上”
            【解决方案9】:

            小心!

            如果您有多个脚本标签,并且您正在搜索标签属性中的信息,那么您必须使用带有 for 循环的 .length 属性,因为它不是一个简单的数组,而是一个 HTMLCollection 对象。

            https://developer.mozilla.org/en/DOM/HTMLCollection

            如果您使用 foreach 语句 for(var i in yourList) 它将在大多数浏览器中返回 HTMLCollection 的属性和方法!

            var scriptTags = document.getElementsByTagName("script");
            
            for(var i = 0; i < scriptTags.length; i++)
            alert(i); // Will print all your elements index (you can get src attribute value using scriptTags[i].attributes[0].value)
            
            for(var i in scriptTags)
            alert(i); // Will print "length", "item" and "namedItem" in addition to your elements!
            

            即使 getElementsByTagName 应该返回一个 NodeList,大多数浏览器也会返回一个 HTMLCollection: https://developer.mozilla.org/en/DOM/document.getElementsByTagName

            【讨论】:

              【解决方案10】:

              使用 Array().forEach 循环来利用并行性

              【讨论】:

              • 浏览器中的 JavaScript 是事件循环并发的,所以 Array.prototype.forEach 不会并行执行多个回调调用。
              【解决方案11】:

              使用 forEach 跳过原型链

              只是对@nailer's answer above 的快速补充,将 forEach 与 Object.keys 一起使用意味着您可以避免迭代原型链而无需使用 hasOwnProperty。

              var Base = function () {
                  this.coming = "hey";
              };
              
              var Sub = function () {
                  this.leaving = "bye";
              };
              
              Sub.prototype = new Base();
              var tst = new Sub();
              
              for (var i in tst) {
                  console.log(tst.hasOwnProperty(i) + i + tst[i]);
              }
              
              Object.keys(tst).forEach(function (val) {
                  console.log(val + tst[val]);
              });
              

              【讨论】:

              • 该死的,这是鬼鬼祟祟的。值得阅读其他 50 篇文章来了解这一点。 obj = {“粉红色”:“鸭子”,红色:“鹅”}; Object.keys(obj) === ["pink", "red"]
              【解决方案12】:

              应根据最容易理解的成语进行选择。

              一个数组被迭代使用:

              for (var i = 0; i < a.length; i++)
                 //do stuff with a[i]
              

              用作关联数组的对象通过以下方式迭代:

              for (var key in o)
                //do stuff with o[key]
              

              除非您有惊天动地的原因,否则请坚持既定的使用模式。

              【讨论】:

              • 应该提到的是,使用 for...in 和过滤 if 语句是一个很好的做法。对象“obj.hasOwnProperty(member)”有一个方便的方法,它检查迭代器返回的成员是否实际上是对象的成员。见:javascript.crockford.com/code.html
              • 正如在另一个答案中评论的那样,“for...in”对于数组不能正常工作,因为它会遍历所有数组属性和方法。因此,您应该只使用“for...in”来迭代对象属性。否则,坚持“for(i=0; i
              • 出于性能原因,IMO 最好在 for 之前评估数组的长度,而不是在循环中每次都评估 a.length。
              • @UpTheCreek:当数组实际上是由 HTMLDOM 返回的东西时,这绝对是正确的,但是,我想知道标准 javascript 数组需要多大才能看到明显的差异?就我个人而言,我会尽可能地简化代码,直到证明有必要做一些不同的事情。
              • @Pichan 我认为你的意思是i &lt; l,而不是i &lt; a,在你的for循环条件下。
              【解决方案13】:

              数组稀疏时两者不一样。

              var array = [0, 1, 2, , , 5];
              
              for (var k in array) {
                // Not guaranteed by the language spec to iterate in order.
                alert(k);  // Outputs 0, 1, 2, 5.
                // Behavior when loop body adds to the array is unclear.
              }
              
              for (var i = 0; i < array.length; ++i) {
                // Iterates in order.
                // i is a number, not a string.
                alert(i);  // Outputs 0, 1, 2, 3, 4, 5
                // Behavior when loop body modifies array is clearer.
              }
              

              【讨论】:

                【解决方案14】:

                请注意,原生 Array.forEach 方法现在是 widely supported

                【讨论】:

                • 它有什么作用?它是否有其他帖子中提到的问题(循环属性而不是数组元素)?另外,由于 IE8 不支持它,所以说它被广泛支持有点牵强。
                • 尽管*nix 用户鄙视它,IE8 是大多数子windows7 用户。这是浏览器市场的很大一部分。
                • @Rauni -- 我同意你的观点,但对于桌面设备,IE 浏览器的份额不到 40%,根据en.wikipedia.org/wiki/Usage_share_of_web_browsers#Summary_table,根据marketshare.hitslink.com/… 和其他网站,至少 8%的浏览器是 IE 9。换句话说,大约 70% 的桌面浏览器支持 Array.forEach,所以我不认为“广泛支持”是不合理的。我还没有检查,但移动支持(在 WebKit 和 Opera 浏览器上)可能更高。显然,地理上存在相当大的差异。
                • 感谢您的更新。我同意可以说它“得到广泛支持”。唯一的问题是,如果用户使用这个 JS 方法,如果不支持,他/她仍然需要为这种情况编写一个备份方法..
                • @Rauni -- 您可以使用 es5-shim 和 es6-shim 自动提供备份方法。 github.com/es-shims/es5-shim
                【解决方案15】:

                如果你真的想加速你的代码,那又如何呢?

                for( var i=0,j=null; j=array[i++]; foo(j) );
                

                这有点像 for 语句中的 while 逻辑,它的冗余度较低。 Firefox 也有 Array.forEach 和 Array.filter

                【讨论】:

                • 为什么这会加速你的代码?我不明白为什么像这样重新排列语句会加快速度。
                【解决方案16】:

                仅供参考 - jQuery 用户


                jQuery 的each(callback) 方法默认使用for( ; ; ) 循环,如果长度为undefined,将使用for( in )

                因此,我会说在使用此功能时假设正确的顺序是安全的。

                示例

                $(['a','b','c']).each(function() {
                    alert(this);
                });
                //Outputs "a" then "b" then "c"
                

                使用它的缺点是,如果您正在执行一些非 UI 逻辑,您的函数将不太适合其他框架。 each() 函数可能最好保留用于 jQuery 选择器,否则建议使用 for( ; ; )


                【讨论】:

                • documentcloud.github.com/underscore 总是有 _.each 和许多其他有用的功能
                • 这是否也意味着如果我的对象 $.each 中有长度属性会失败?例如x= { a:"1", b:"2", 长度:3 }.
                【解决方案17】:

                这是我做的。

                function foreach(o, f) {
                 for(var i = 0; i < o.length; i++) { // simple for loop
                  f(o[i], i); // execute a function and make the obj, objIndex available
                 }
                }
                

                这就是你将如何使用它
                这将适用于数组和对象(例如 HTML 元素列表)

                foreach(o, function(obj, i) { // for each obj in o
                  alert(obj); // obj
                  alert(i); // obj index
                  /*
                    say if you were dealing with an html element may be you have a collection of divs
                  */
                  if(typeof obj == 'object') { 
                   obj.style.marginLeft = '20px';
                  }
                });
                

                我刚做了这个,所以我愿意接受建议:)

                【讨论】:

                • 好东西 - 非常简单!
                【解决方案18】:

                我已经看到使用对象、原型和数组的“for each”问题

                我的理解是 for each 用于对象的属性而不是数组

                【讨论】:

                  【解决方案19】:

                  数组上的 For in 循环与 Prototype 不兼容。如果您认为将来可能需要使用该库,那么坚持使用 for 循环是有意义的。

                  http://www.prototypejs.org/api/array

                  【讨论】:

                  • 忘记“你可能需要使用那个库”。相反,请考虑“您的 JS 可能包含在使用该库的其他任何东西中”,因为问题仍然存在。
                  【解决方案20】:

                  根据您使用的循环类型和浏览器的不同,性能会有所不同。

                  例如:

                  for (var i = myArray.length-1; i >= 0; i--)
                  

                  在某些浏览器上几乎是两倍:

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

                  但是,除非您的阵列非常庞大,或者您不断循环它们,否则所有阵列都足够快。我严重怀疑数组循环是您项目(或任何其他项目)中的瓶颈

                  【讨论】:

                  • 在循环之前将“myArray.length”存储到变量中会使性能差异消失吗?我的猜测是“是”。
                  • 没有。 'myArray.length' 是一个属性,而不是对象上的方法——没有进行计算来确定它的值。将其值存储在变量中根本不会做任何事情。
                  • 是的。属性不是变量;他们确实有获取/设置代码。
                  • 我倾向于使用for(var i = myArray.length; i--;)
                  • 代码清晰易读。不是因为在当前时间使用荒谬的大量数组时,在某些浏览器中运行速度会稍微快一些。优化可能会改变,但您的代码的可读性(或缺乏可读性)不会。编写其他人可以轻松遵循的代码,并让优化人员在自己的适当时间赶上。
                  【解决方案21】:

                  我会根据我想要引用项目的方式使用不同的方法。

                  如果您只想要当前项目,请使用 foreach。

                  如果您需要索引器进行相对比较,请使用 for。 (即,这与上一个/下一个项目相比如何?)

                  我从未注意到性能差异。我会等到出现性能问题后再担心。

                  【讨论】:

                  • 请参阅下面的 Bnos 答案 - 因为...in 并没有按照您的预期进行,如果您使用它,您可能会获得各种乐趣。郑重声明,Prototype 以正确的方式做事。
                  【解决方案22】:

                  使用 for (var i in myArray) 你也可以遍历对象,i 将包含键名,你可以通过 myArray[我]。此外,您将添加到对象的任何方法也将包含在循环中,即,如果您使用任何外部框架(如 jQuery 或原型),或者如果您直接向对象原型添加方法,则在某一时刻 i 将指向这些方法。

                  【讨论】:

                    猜你喜欢
                    • 2011-07-12
                    • 2012-11-18
                    • 1970-01-01
                    • 2016-11-09
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2010-10-12
                    相关资源
                    最近更新 更多