JavaScript für… in vs für

461

Denken Sie, dass es einen großen Unterschied zwischen for ... in und for-Schleifen gibt? Welche Art von "für" bevorzugen Sie und warum?

Angenommen, wir haben ein Array von assoziativen Arrays:

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

So können wir iterieren:

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

Und:

for (var i in myArray)

Ich sehe keinen großen Unterschied. Gibt es Leistungsprobleme?

andrii
quelle
13
Beachten Sie, dass wir ab JS 1.6 auch : myArray.forEach(callback[, thisarg]).
Benji XVI
14
@ Benji array.forEach ist tatsächlich in ES5.
Mikemaccana
2
In einer For-In-Schleife benötigen Sie eine Bedingung, die wie if(myArray.hasOwnProperty(i)){true}
folgt
6
['foo', 'bar', 'baz'].forEach(function(element, index, array){ console.log(element, index, array); }); ist in Ordnung, so ziemlich überall zu verwenden, außer in IE8- und es ist bei weitem die eleganteste Syntax
Jon z
5
Es gibt auch eine for...ofAussage in ECMAScript 6 , zum Beispiel:for (let i of myArray) console.log(i);
Vitalii Fedorenko

Antworten:

548

Die Wahl sollte auf der Sprache basieren, die am besten verstanden wird.

Ein Array wird iteriert mit:

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

Ein Objekt, das als assoziatives Array verwendet wird, wird iteriert mit:

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

Halten Sie sich an das etablierte Verwendungsmuster, es sei denn, Sie haben erderschütternde Gründe.

AnthonyWJones
quelle
38
Es sollte erwähnt werden, dass es eine gute Übung ist, ... mit der Filterung der if-Anweisung zu verwenden. Es gibt eine praktische Methode für das Objekt "obj.hasOwnProperty (member)", mit der überprüft wird, ob ein vom Iterator zurückgegebenes Mitglied tatsächlich Mitglied des Objekts ist. Siehe: javascript.crockford.com/code.html
Damir Zekić
57
Wie bei anderen Antworten kommentiert, funktioniert das "for ... in" für Arrays nicht richtig, da es alle Array-Eigenschaften und -Methoden durchläuft. Daher sollten Sie "for ... in" nur zum Durchlaufen von Objekteigenschaften verwenden. Andernfalls bleiben Sie bei "für (i = 0; i <etwas; i ++)"
Denilson Sá Maia
Aus Leistungsgründen ist es IMO besser, die Länge des Arrays vor dem zu forbewerten und nicht jedes Mal in der Schleife eine Länge zu bewerten .
UpTheCreek
9
@UpTheCreek: Das ist definitiv wahr, wenn das Array tatsächlich etwas ist, das vom HTMLDOM zurückgegeben wird. Ich frage mich jedoch, wie groß ein Standard-Javascript-Array sein müsste, bevor Sie einen nennenswerten Unterschied feststellen können. Persönlich würde ich den Code so einfach wie möglich halten, bis es sich als notwendig erwiesen hat, etwas anderes zu tun.
AnthonyWJones
2
@Pichan Ich denke du meinst i < lnicht i < ain deinem For-Loop-Zustand.
Max Nanasy
161

Douglas Crockford empfiehlt in JavaScript: The Good Parts (Seite 24), die Verwendung der for inAnweisung zu vermeiden .

Wenn Sie die for inEigenschaftsnamen in einem Objekt durchlaufen, werden die Ergebnisse nicht sortiert. Schlimmer noch: Möglicherweise erhalten Sie unerwartete Ergebnisse. Es enthält Mitglieder, die von der Prototypenkette geerbt wurden, und den Namen der Methoden.

Alles außer den Eigenschaften kann mit herausgefiltert werden .hasOwnProperty. Dieses Codebeispiel macht das, was Sie ursprünglich wahrscheinlich wollten:

for (var name in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, name)) {
        // DO STUFF
    }
}
Benno Richters
quelle
70
for ... in eignet sich perfekt zum Durchlaufen von Objekteigenschaften. Es ist nicht zum Durchlaufen von Array-Elementen geeignet. Wenn Sie den Unterschied zwischen diesen Szenarien nicht verstehen können, sollten Sie dies vermeiden für ... in; sonst verrückt werden.
Shog9
8
Ich möchte betonen, dass es NICHT BESTELLT ist! Dies könnte ein großes Problem sein und wäre schwer zu fangen.
Jason
4
+1 für "Es enthält Mitglieder, die von der Prototypenkette geerbt wurden, und den Namen der Methoden." Sie werden Spaß haben, wenn jemand Ihren Code mit geladenem Prototyp verwendet (auch wenn Ihr Code ihn nicht wirklich verwendet).
ijw
13
Bitte vergessen Sie nicht , die nameVariable zu deklarierenfor(var name in object)... . Andernfalls befindet sich die Variable , wenn sie sich beispielsweise in einer Funktion befindet name, eine Eigenschaft des globalen Objekts (eine Zuweisung zu einem nicht deklarierten Bezeichner übernimmt dies), auch im neuen ECMAScript 5 Strenger Modus, dieser Code wirft a ReferenceError.
CMS
6
@Nosredna: Es gibt ein Problem mit der Reihenfolge der Iteration für Chrome, das von keinem anderen als John Resig eingereicht wurde und als WontFix gekennzeichnet ist. code.google.com/p/chromium/issues/detail?id=883 . Noch vor Chrome war die Reihenfolge der Iteration in allen Browsern nicht dieselbe, wenn Sie eine Eigenschaft entfernen und dann erneut hinzufügen. Auch IE 9 verhält sich sehr ähnlich wie Chrom (angeblich zur Geschwindigkeitsverbesserung). Also ... Bitte hör auf, ungenaue Informationen zu verbreiten, du wärst sehr naiv, wenn du davon abhängig bist.
Juan Mendes
62

Zu Ihrer Information - jQuery-Benutzer


Die each(callback)Methode for( ; ; )von jQuery verwendet standardmäßig eine Schleife und wird for( in ) nur verwendet, wenn die Länge gleich ist undefined.

Daher würde ich sagen, dass es sicher ist, bei Verwendung dieser Funktion die richtige Reihenfolge anzunehmen.

Beispiel :

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

Der Nachteil bei der Verwendung ist, dass Ihre Funktionen für andere Frameworks weniger portabel sind, wenn Sie eine Nicht-UI-Logik verwenden. Die each()Funktion ist wahrscheinlich am besten für die Verwendung mit jQuery-Selektoren reserviert und for( ; ; )kann ansonsten empfohlen werden.


Jason
quelle
4
Es gibt immer documentcloud.github.com/underscore, das _.each und viele andere nützliche Funktionen hat
w00t
1
Bedeutet das auch, wenn mein Objekt die Eigenschaft length hat? $ .each schlägt fehl? zB x = {a: "1", b: "2", Länge: 3}.
Onur Topal
29

Es gibt Leistungsunterschiede, je nachdem, welche Art von Schleife Sie verwenden und in welchem ​​Browser.

Zum Beispiel:

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

ist in einigen Browsern fast doppelt so schnell wie:

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

Wenn Ihre Arrays jedoch nicht RIESIG sind oder Sie sie ständig schleifen, sind alle schnell genug. Ich bezweifle ernsthaft, dass Array-Looping ein Engpass in Ihrem Projekt ist (oder für jedes andere Projekt in dieser Angelegenheit).

Gen
quelle
5
Würde das Speichern von "myArray.length" in einer Variablen vor dem Schleifen den Leistungsunterschied beseitigen? Meine Vermutung ist "Ja".
Tomalak
3
Nein. 'MyArray.length' ist eine Eigenschaft, keine Methode für ein Objekt. Es wird keine Berechnung durchgeführt, um dessen Wert zu bestimmen. Das Speichern des Werts in einer Variablen führt zu nichts.
Jason
3
Ja da ist. Eigenschaften sind keine Variablen; Sie haben get / set Code.
ste
12
Ich neige dazu zu verwendenfor(var i = myArray.length; i--;)
Kevin
2
Code für Klarheit und Lesbarkeit. Nicht für das, was in einigen Browsern geringfügig schneller läuft, wenn derzeit absurd massive Arrays verwendet werden. Optimierungen können sich ändern, die Lesbarkeit (oder das Fehlen) Ihres Codes jedoch nicht. Schreiben Sie Code, dem andere problemlos folgen können, und lassen Sie die Optimierer rechtzeitig nachholen.
aroth
26

Beachten Sie, dass die native Array.forEach-Methode jetzt weitgehend unterstützt wird .

Sam Dutton
quelle
2
Was tut es? Hat es die in anderen Posts erwähnten Probleme (Schleifen über Eigenschaften anstelle von Array-Elementen)? Da IE8 dies nicht unterstützt, ist es ein bisschen schwierig zu sagen, dass es weitgehend unterstützt wird.
Rauni Lillemets
2
So sehr * nix-Benutzer es verachten, IE8 ist vor allem Sub-Windows7-Benutzer. Das ist ein massiver Teil des Browsermarktes.
Sbartell
2
@Rauni - Ich nehme Ihren Standpunkt an, aber für Desktop-Geräte beträgt der Anteil des IE-Browsers laut en.wikipedia.org/wiki/Usage_share_of_web_browsers#Summary_table und gemäß marketshare.hitslink.com/… und anderen Websites weniger als 40%. Mindestens 8% der Browser sind IE 9. Mit anderen Worten, Array.forEach wird von etwa 70% der Desktop-Browser unterstützt, sodass ich nicht der Meinung bin, dass eine breite Unterstützung unangemessen ist. Ich habe es nicht überprüft, aber die mobile Unterstützung (in WebKit- und Opera-Browsern) ist möglicherweise noch höher. Offensichtlich gibt es geografisch erhebliche Unterschiede.
Sam Dutton
1
Danke für das Update. Ich stimme zu, dass man sagen könnte, dass es "weithin unterstützt" wird. Das einzige Problem ist, dass der Benutzer, wenn er diese JS-Methode verwendet, immer noch eine
Sicherungsmethode
1
@Rauni - Sie können es5-shim und es6-shim verwenden, um automatisch Sicherungsmethoden bereitzustellen. github.com/es-shims/es5-shim
Michiel van der Blonk
24

Die aktualisierte Antwort für die aktuelle Version 2012 aller gängigen Browser - Chrome, Firefox, IE9, Safari und Opera - unterstützt das native Array.forEach von ES5.

Sofern Sie keinen Grund haben, IE8 nativ zu unterstützen (unter Berücksichtigung der Tatsache, dass diesen Benutzern ES5-Shim oder Chrome-Frame zur Verfügung gestellt werden können, um eine ordnungsgemäße JS-Umgebung bereitzustellen), ist es sauberer, einfach die richtige Syntax der Sprache zu verwenden:

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

Die vollständige Dokumentation für array.forEach () finden Sie unter MDN.

Mikemaccana
quelle
1
Sie sollten wirklich die Parameter des Rückrufs dokumentieren: 1. den Elementwert, 2. den Elementindex, 3. das Array, das durchlaufen wird
zog am
Ich höre, was Sie sagen, aber in diesem Fall verdeckt eine zu starke Vereinfachung die gesamte Bandbreite der Möglichkeiten. Wenn sowohl der Index als auch der Wert vorhanden sind, kann dies als Ersatz für ... in und für jedes ... in dienen - mit dem Bonus, dass Sie sich nicht merken müssen, welche Schlüssel oder Werte durchlaufen werden.
Zeichnen
1
@Cory: ES5 forEach kann relativ einfach zu älteren ES3-Browsern hinzugefügt werden. Weniger Code ist besserer Code.
Mikemaccana
2
@nailer Kann dies für Arrays und Objekte austauschbar verwendet werden?
Hitautodestruct
1
@hitautodestruct Es ist Teil des Prototyps von Array, nicht des von Object. Im Allgemeinen werden Nicht-Array-Objekte in der Community derzeit noch mit 'for (var key in object) {}' iteriert.
Mikemaccana
14

Die beiden sind nicht gleich, wenn das Array dünn ist.

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.
}
Mike Samuel
quelle
14

Verwenden von forEach zum Überspringen der Prototypenkette

Nur eine kurze Ergänzung zu der Antwort von @ nailer oben : Wenn Sie forEach mit Object.keys verwenden, können Sie vermeiden, dass Sie über die Prototypenkette iterieren, ohne hasOwnProperty verwenden zu müssen.

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]);
});
Meloncholie
quelle
2
Verdammt, das ist hinterhältig. Es hat sich gelohnt, 50 andere Beiträge zu lesen, um dazu zu gelangen. obj = {"pink": "ducks", rot: "gänse"}; Object.keys (obj) === ["pink", "red"]
Orwellophile
14

Ich bin der Meinung, dass Sie die Iterationsmethode entsprechend Ihren Anforderungen auswählen sollten. Ich würde vorschlagen, dass Sie eigentlich nie native Arraymit for inStruktur durchlaufen . Es ist viel langsamer und , wie Chase Seibert im Moment betonte, nicht mit dem Prototype-Framework kompatibel.

Es gibt einen hervorragenden Benchmark für verschiedene Schleifenstile, den Sie unbedingt beachten sollten, wenn Sie mit JavaScript arbeiten . Nehmen Sie keine frühen Optimierungen vor, aber Sie sollten das Zeug irgendwo im Hinterkopf behalten.

Ich würde verwenden for in, um alle Eigenschaften eines Objekts abzurufen, was besonders beim Debuggen Ihrer Skripte nützlich ist. Zum Beispiel möchte ich diese Zeile zur Hand haben, wenn ich unbekannte Objekte erkunde:

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

Es speichert den Inhalt des gesamten Objekts (zusammen mit den Methodenkörpern) in meinem Firebug-Protokoll. Sehr praktisch.

Damir Zekić
quelle
Link ist jetzt unterbrochen. Sicher würde ich gerne den Benchmark sehen, wenn jemand einen anderen Link hat.
Billbad
Es ist nicht mehr kaputt.
Olli
Foreach-Schleifen brechen im Prototyp? Da dies jetzt allgemein unterstützt wird, sollte dieser Prototyp gelöst werden.
MVrak
7

Hier ist etwas, was ich getan habe.

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

So würden Sie es verwenden.
Dies funktioniert bei Arrays und Objekten (z. B. einer Liste von HTML-Elementen).

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

Ich habe das gerade gemacht, also bin ich offen für Vorschläge :)


quelle
Tolles Zeug - ziemlich einfach!
Chris
6

Ich würde die verschiedenen Methoden verwenden, basierend darauf, wie ich auf die Elemente verweisen wollte.

Verwenden Sie foreach, wenn Sie nur das aktuelle Element möchten.

Verwenden Sie diese Option, wenn Sie einen Indexer für relative Vergleiche benötigen. (Dh wie ist das im Vergleich zum vorherigen / nächsten Artikel?)

Ich habe noch nie einen Leistungsunterschied bemerkt. Ich würde warten, bis ein Leistungsproblem auftritt, bevor ich mir darüber Sorgen mache.

Matt Lacey
quelle
Siehe Bnos Antwort unten - denn ... in tut nicht das, was Sie hier erwarten, und wenn Sie es verwenden, haben Sie möglicherweise alle Arten von Spaß. Für die Aufzeichnung macht Prototype die Dinge richtig .
marcus.greasly
4

Mit for (var i in myArray) können Sie auch Objekte durchlaufen , ich werde den Schlüsselnamen enthalten und Sie können über myArray [i] auf die Eigenschaft zugreifen . Zusätzlich werden alle Methoden, die Sie dem Objekt hinzugefügt haben, ebenfalls in die Schleife aufgenommen, dh wenn Sie ein externes Framework wie jQuery oder Prototype verwenden oder wenn Sie Methoden direkt zu Objektprototypen hinzufügen, an einem Punkt, auf den ich verweise diese Methoden.

pilsetnieks
quelle
4

Achtung!

Wenn Sie mehrere Skript-Tags haben und beispielsweise Informationen in Tag-Attributen suchen, müssen Sie die Eigenschaft .length mit einer for-Schleife verwenden, da es sich nicht um ein einfaches Array, sondern um ein HTMLCollection-Objekt handelt.

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

Wenn Sie die foreach-Anweisung für (var i in yourList) verwenden, werden in den meisten Browsern Eigenschaften und Methoden der HTMLCollection zurückgegeben!

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!

Selbst wenn getElementsByTagName eine NodeList zurückgeben sollte, geben die meisten Browser eine HTMLCollection zurück: https://developer.mozilla.org/en/DOM/document.getElementsByTagName

Taufe
quelle
3

Denn in-Schleifen auf Arrays sind nicht mit Prototype kompatibel. Wenn Sie der Meinung sind, dass Sie diese Bibliothek in Zukunft möglicherweise verwenden müssen, ist es sinnvoll, sich an for-Schleifen zu halten.

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

Chase Seibert
quelle
Vergessen Sie "Möglicherweise müssen Sie diese Bibliothek verwenden". Denken Sie stattdessen "Ihr JS könnte in alles andere enthalten sein , das diese Bibliothek verwendet", da die Probleme immer noch auftreten.
ijw
3

Ich habe Probleme mit dem "für jeden" bei der Verwendung von Objekten, Prototypen und Arrays gesehen

Mein Verständnis ist, dass das für jedes für Eigenschaften von Objekten und NICHT Arrays ist

Benjamin Lee
quelle
3

Wenn Sie Ihren Code wirklich beschleunigen möchten, was ist damit?

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

Es ist eine Art while-Logik in der for-Anweisung und weniger redundant. Firefox hat auch Array.forEach und Array.filter

Fabjoa
quelle
2
Warum sollte dies Ihren Code beschleunigen? Ich kann nicht verstehen, warum das Umschreiben solcher Aussagen es beschleunigen würde.
Rup
3

Ein kürzerer und bester Code laut jsperf ist

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

Verwenden Sie die Array (). ForEach-Schleife, um die Parallelität zu nutzen

PazoozaTest Pazman
quelle
4
JavaScript im Browser ist eine gleichzeitige Ereignisschleife, sodass Array.prototype.forEachnicht mehrere Aufrufe des Rückrufs gleichzeitig ausgeführt werden.
Mike Samuel
1

für (;;) ist für Arrays : [20,55,33]

for..in ist für Objekte : {x: 20, y: 55: z: 33}

Angelito
quelle
0

Achtung!!! Ich verwende Chrome 22.0 unter Mac OS und habe Probleme mit der Syntax für jede Syntax.

Ich weiß nicht, ob dies ein Browserproblem, ein Javascript-Problem oder ein Fehler im Code ist, aber es ist SEHR seltsam. Außerhalb des Objekts funktioniert es perfekt.

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
Paulo Check
quelle
0

Es gibt einen wichtigen Unterschied zwischen beiden. Das For-In durchläuft die Eigenschaften eines Objekts. Wenn es sich also um ein Array handelt, durchläuft es nicht nur seine Elemente, sondern auch die Funktion "Entfernen".

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

Sie können das For-In mit einem verwenden if(myArray.hasOwnProperty(i)). Wenn ich über Arrays iteriere, ziehe ich es jedoch immer vor, dies zu vermeiden und nur die for (;;) - Anweisung zu verwenden.

Achecopar
quelle
0

Obwohl beide sehr ähnlich sind, gibt es einen kleinen Unterschied:

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
}

In diesem Fall lautet die Ausgabe:

STANDARD FÜR LOOP:

ARRAY [0] = A.

ARRAY [1] = B.

ARRAY [2] = C.

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

In diesem Fall lautet die Ausgabe:

FOR-IN-SCHLEIFE:

ARRAY [1] = B.

ARRAY [2] = C.

ARRAY [10] = D.

ARRAY [ABC] = 123

Yash Srivastava
quelle
0

Die for in-Anweisung ermöglicht das Durchlaufen der Namen aller Eigenschaften eines Objekts. Leider durchläuft es auch alle Mitglieder, die durch die Prototypenkette geerbt wurden. Dies hat den schlechten Nebeneffekt, dass Methodenfunktionen bereitgestellt werden, wenn das Interesse an den Datenelementen liegt.

Fayaz
quelle