JavaScript for…in vs for


461

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

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

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

这样我们就可以迭代:

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

和:

for (var i in myArray)

我没什么大不同。有性能问题吗?


13
请注意,我们也为JS 1.6的,有:myArray.forEach(callback[, thisarg])
Benji XVI

14
@Benji array.forEach实际上在ES5中。
mikemaccana 2012年

2
在for-in循环中,您需要一个如下条件:if(myArray.hasOwnProperty(i)){true}
Eric Hodonsky 2012年

6
['foo', 'bar', 'baz'].forEach(function(element, index, array){ console.log(element, index, array); }); 可以在IE8以外的任何地方使用几乎所有内容,这是迄今为止最优雅的语法
Jon z

5
ECMAScript 6中也有for...of声明,例如:for (let i of myArray) console.log(i);
Vitalii Fedorenko

Answers:


548

选择应该基于对哪种习语有最好的了解。

使用以下方法迭代数组:

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

使用以下方法来迭代用作关联数组的对象:

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

除非您有惊天动地的理由,否则请遵循既定的使用模式。


38
应该提到的是,将...用于过滤if语句是一种不错的做法。有一个方便的对象“ obj.hasOwnProperty(member)”方法,该方法检查迭代器返回的成员是否实际上是该对象的成员。请参阅:javascript.crockford.com/code.html
丹尼尔·泽基奇(DamirZekić)

57
如另一个答案所述,“ for ... in”不适用于数组,因为它将遍历所有数组属性和方法。因此,只应在对对象属性进行迭代时使用“ for ... in”。否则,请坚持“ for(i = 0; i <something; i ++)”
DenilsonSáMaia

出于性能原因,IMO最好在之前for评估数组的长度,而不是每次循环都评估a.length。
UpTheCreek 2012年

9
@UpTheCreek:当数组实际上是HTMLDOM返回的东西时,这绝对是正确的,但是,我想知道看到明显的区别之前,标准的javascript数组需要多大?就我个人而言,我将使代码尽可能地简单,直到证明其有必要执行其他操作为止。
AnthonyWJones

2
@Pichan我认为您的意思i < l不是i < afor循环条件。
Max Nanasy

161

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
    }
}

70
for ... in非常适合循环遍历对象属性。它不适用于循环遍历数组元素。如果您无法理解这两种情况之间的区别,那么可以,请避免使用...。否则,发疯。
Shog9

8
要强调的事实是它不被订购!这可能是一个大问题,并且很难捕获到错误。
杰森

4
+1表示“它包括从原型链继承的成员和方法的名称。” 例如,如果有人碰巧使用了加载了Prototype的代码(即使您的代码实际上并未使用它),您也会很有趣。
ijw

13
请不要忘了声明name变量:for(var name in object)...,否则,如果该代码是例如函数里面,name变量最终被全局对象(分配到的属性未声明的标识符不说),也是在新的ECMAScript 5严格模式下,该代码将引发ReferenceError
CMS 2010年

6
@Nosredna:Chrome的迭代顺序存在一个问题,该问题由John Resig提出,被标记为WontFix。code.google.com/p/chromium/issues/detail?id=883。即使在chrome之前,如果先删除然后再添加属性,迭代顺序在浏览器之间也不相同。IE 9的行为也很像chrome(据说是为了提高速度)。所以...请停止散布不正确的信息,您会很天真地依赖它。
Juan Mendes

62

仅供参考-jQuery用户


jQuery的each(callback)方法for( ; ; )默认情况下使用循环,并且for( in ) 在长度为时才使用undefined

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

范例

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

使用此方法的缺点是,如果您要执行一些非UI逻辑,则您的函数将难以移植到其他框架中。该each()函数可能最好保留给jQuery选择器使用,for( ; ; )否则建议这样做。



4
总有documentcloud.github.com/underscore具有_.each和很多其他有用的功能
w00t

1
这是否还意味着如果我的对象$ .each中具有length属性,每个对象都会失败?例如x = {a:“ 1”,b:“ 2”,length:3}。
Onur Topal 2013年

29

根据使用哪种循环以及使用哪种浏览器,性能会有差异。

例如:

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

在某些浏览器中的速度几乎是:

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

但是,除非您的数组很大或不断循环它们,否则它们都足够快。我严重怀疑数组循环是否会成为您的项目(或与此相关的任何其他项目)的瓶颈


5
在循环之前将“ myArray.length”存储到变量中会使性能差异消失吗?我的猜测是“是”。
Tomalak

3
否。“ myArray.length”是属性,而不是对象上的方法,无法进行计算以确定其值。将其值存储在变量中将什么都不做。
杰森

3
就在这里。属性不是变量;他们确实有获取/设置代码。
STE

12
我倾向于使用for(var i = myArray.length; i--;)
凯文(Kevin)2010年

2
代码清晰易读。在当前时间使用荒谬的大型阵列时,某些浏览器运行速度会稍快一点。优化可能会更改,但是代码的可读性(或缺乏可读性)不会改变。编写其他人可以轻松遵循的代码,并让优化器在适当的时候赶上来。
aroth 2015年

26

请注意,本机Array.forEach方法现在得到广泛支持


2
它有什么作用?是否存在其他帖子中提到的问题(遍历属性而不是数组元素)?此外,由于IE8不支持它,因此可以说它得到了广泛支持是有点困难的事情。
拉尼·利梅梅斯

2
尽管* nix用户鄙视它,但IE8是所有sub-windows7用户中的大多数。多数民众赞成在浏览器市场的很大一部分。
sbartell

2
@Rauni-我同意你的观点,但是根据en.wikipedia.org/wiki/Usage_share_of_web_browsers#Summary_tablemarketshare.hitslink.com/…等网站,对于台式机设备,IE浏览器的份额不到40%,至少8%的浏览器是IE9。换句话说,大约70%的桌面浏览器都支持Array.forEach,因此我认为“广泛支持”是不合理的。我还没有检查过,但移动支持(在WebKit和Opera浏览器上)可能更高。显然,在地理位置上存在很大差异。
山姆·达顿

1
感谢更新。我同意可以说它得到“广泛支持”。唯一的问题是,如果用户使用这个JS方法,他/她仍然有写的情况下的备份方法,如果不支持它..
Rauni Lillemets

1
@Rauni-您可以使用es5-shim和es6-shim自动提供备份方法。github.com/es-shims/es5-shim
Michiel van der Blonk 2015年

24

更新了适用于所有主要浏览器的2012当前版本的答案 -Chrome,Firefox,IE9,Safari和Opera支持ES5的本机array.forEach。

除非您有某些本机支持IE8的理由(请记住可以为这些用户提供ES5-shim或Chrome框架,这将提供适当的JS环境),否则使用该语言的适当语法会更干净:

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

有关array.forEach()的完整文档,请访问MDN。


1
您应该真正记录回调的参数:1st元素值,2nd元素索引,3rd被遍历的数组
2012年

我听到您在说什么,但是在这种情况下,过分简化会掩盖所有可能性。同时拥有索引和值意味着可以用... in和每个... in代替它,而且不必记住要在键或值上进行迭代的好处。
2012年

1
@Cory:ES5 forEach可以很容易地添加到旧版ES3浏览器中。更少的代码就是更好的代码。
mikemaccana

2
@nailer可以在数组和对象上互换使用吗?
hitautodestruct

1
@hitautodestruct它是Array原型的一部分,而不是Object的一部分。通常在社区中,现在非数组对象仍然使用'for(对象中的var键){}'进行迭代。
mikemaccana

14

数组稀疏时,两者不相同。

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

使用forEach跳过原型链

只是上述@nailer答案的快速附录,使用带Object.keys的forEach意味着您可以避免对原型链进行迭代,而不必使用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]);
});

2
该死的,这是偷偷摸摸的。值得一读其他50篇文章。obj = {“ pink”:“鸭子”,红色:“鹅”}; Object.keys(obj)=== [“粉红色”,“红色”]
Orwellophile 2012年

14

我第二意见是,您应该根据需要选择迭代方法。我真的建议你不要永远通过本地环路Arrayfor in结构。正如Chase Seibert之前指出的那样,它的速度要慢得多,并且与Prototype框架不兼容。

如果您使用JavaScript,绝对有一个关于不同循环样式的出色基准,您绝对应该看一下。不要进行早期优化,但是您应该将这些东西放在脑后。

我会使用它for in来获取对象的所有属性,这在调试脚本时特别有用。例如,当我探索陌生的对象时,我希望这行很方便:

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

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


链接现在已断开。如果有人有另一个链接,当然希望看到基准。
比尔巴德(Billbad)2012年

它不再坏了。
Olli 2012年

Foreach循环是否在原型中中断?正如现在普遍支持的那样,原型应该解决的问题。
mvrak

7

这是我做的。

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';
  }
});

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


好东西-非常简单!
克里斯,

6

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

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

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

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


请参阅下面的Bnos答案-因为...没有按照您在这里的预期做,如果您使用它,则可能会充满各种乐趣。记录下来,Prototype以正确的方式做事。
marcus.greasly

4

使用for(myArray中的var i),您也可以循环访问对象,将包含键名,并且可以通过myArray [i]访问该属性。另外,您将添加到对象的任何方法也将包含在循环中,即,如果您使用任何外部框架(如jQuery或原型),或者如果直接将方法添加到对象原型,则将指向这些方法。


4

小心!

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

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

如果将foreach语句用于(list中的var i),它将在大多数浏览器中返回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


3

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

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


忘记“您可能需要使用该库”。相反,请考虑“您的JS可能包含在使用该库的任何其他程序中”,因为问题仍然存在。
ijw

3

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

我的理解是for for是对象的属性,而不是数组的属性


3

如果您真的想加快代码执行速度,那又如何呢?

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

这有点在for语句中包含while逻辑,并且它的冗余度较低。Firefox也具有Array.forEach和Array.filter


2
为什么这会加速您的代码?我不明白为什么这样的协调语句会加快速度。
Rup

3

根据jsperf,较短和最佳的代码是

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

1

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


4
浏览器中的JavaScript是事件循环并发的,因此Array.prototype.forEach不会并行执行对回调的多次调用。
Mike Samuel

1

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

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


0

小心!!!我在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

0

两者之间有重要区别。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(;;)语句。


0

尽管它们都非常相似,但还是有细微的差别:

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
}

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

转入圈:

数组[1] = B

数组[2] = C

阵列[10] = D

数组[ABC] = 123


0

for in语句允许遍历对象的所有属性的名称。不幸的是,它还会遍历通过原型链继承的所有成员。当关注数据成员时,这不利于提供方法功能。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.