2083 Stimmen

Warum ist es keine gute Idee, "for...in" für die Iteration von Arrays zu verwenden?

Ich wurde darauf hingewiesen, dass ich in JavaScript for...in nicht mit Arrays verwenden soll. Warum?

54 Stimmen

Ich habe die kürzlich gestellte Frage gesehen, bei der jemand das zu Ihnen gesagt hat, aber sie meinten nur für Arrays. Es gilt als schlechte Praxis, durch Arrays zu iterieren, aber nicht unbedingt für die Iteration durch Elemente eines Objekts.

22 Stimmen

Viele Antworten mit "for" -Schleifen wie 'for (var i=0; i

3 Stimmen

@MarkSchultheiss Aber das ist eine Rückwärtsschleife. Gibt es eine andere Version der Vorwärtsschleife, die schneller ist?

1735voto

Kenan Banks Punkte 196831

Der Grund dafür ist, dass ein Konstrukt:

var a = []; // Erstellt ein neues leeres Array.
a[5] = 5;   // Völlig legaler JavaScript-Code, der das Array neu dimensioniert.

for (var i = 0; i < a.length; i++) {
    // Iteriert über numerische Indizes von 0 bis 5, wie von jedem erwartet wird.
    console.log(a[i]);
}

/* Wird anzeigen:
   undefined
   undefined
   undefined
   undefined
   undefined
   5
*/

kann manchmal völlig anders sein als das andere:

var a = [];
a[5] = 5;
for (var x in a) {
    // Zeigt nur den explizit gesetzten Index "5" an und ignoriert 0-4
    console.log(x);
}

/* Wird anzeigen:
   5
*/

Beachten Sie auch, dass JavaScript-Bibliotheken möglicherweise Dinge wie diese tun, die sich auf jedes von Ihnen erstellte Array auswirken können:

// Irgendwo tief in Ihrer JavaScript-Bibliothek...
Array.prototype.foo = 1;

// Jetzt haben Sie keine Ahnung, was der folgende Code tun wird.
var a = [1, 2, 3, 4, 5];
for (var x in a){
    // Nun ist foo in JEDEM Array enthalten und
    // wird hier als Wert von 'x' angezeigt.
    console.log(x);
}

/* Wird anzeigen:
   0
   1
   2
   3
   4
   foo
*/

428 Stimmen

Denken Sie daran, (var x in a) anstelle von (x in a) zu verwenden - möchten keine globale Variable erstellen.

94 Stimmen

Das erste Problem ist nicht schlecht, sondern nur ein Unterschied in der Semantik. Das zweite Problem scheint mir ein Grund (neben den Konflikten zwischen Bibliotheken, die dasselbe tun) zu sein, weshalb das Ändern des Prototyps eines integrierten Datentyps schlecht ist, anstatt dass for..in schlecht ist.

3 Stimmen

@Stewart. Der 'Unterschied in der Semantik' ist tatsächlich ein Fehler in Javascript. Die fehlerhafte Semantik bedeutet, dass der Code nicht das tut, was du denkst, und deshalb wird die Konstruktion in der Regel vermieden.

432voto

Christian C. Salvadó Punkte 763569

Die for-in-Anweisung an sich ist keine "schlechte Praxis", jedoch kann sie falsch verwendet werden, zum Beispiel um über Arrays oder array-ähnliche Objekte zu iterieren.

Das Ziel der for-in-Anweisung ist es, über Objekteigenschaften zu iterieren. Diese Anweisung wird die Prototypenkette hochgehen, auch über geerbte Eigenschaften iterieren, was manchmal nicht erwünscht ist.

Außerdem ist die Reihenfolge der Iteration durch die Spezifikation nicht garantiert, was bedeutet, dass wenn Sie ein Array-Objekt "iterieren" möchten, mit dieser Anweisung können Sie nicht sicher sein, dass die Eigenschaften (Array-Indizes) in numerischer Reihenfolge besucht werden.

Zum Beispiel, in JScript (IE <= 8), ist die Reihenfolge der Aufzählung sogar bei Array-Objekten definiert, wie die Eigenschaften erstellt wurden:

var array = [];
array[2] = 'c';
array[1] = 'b';
array[0] = 'a';

for (var p in array) {
  //... p wird bei IE "2", "1" und "0" sein
}

Auch, wenn es um geerbte Eigenschaften geht, wenn Sie beispielsweise das Array.prototype Objekt erweitern (wie einige Bibliotheken wie MooTools es tun), werden diese Eigenschaften auch aufgezählt:

Array.prototype.last = function () { return this[this.length-1]; };

for (var p in []) { // ein leeres Array
  // last wird aufgezählt werden
}

Wie ich schon sagte, um über Arrays oder array-ähnliche Objekte zu iterieren, ist es am besten, eine sequenzielle Schleife zu verwenden, wie zum Beispiel eine einfache for/while Schleife.

Wenn Sie nur die eigenen Eigenschaften eines Objekts aufzählen möchten (die nicht geerbt sind), können Sie die hasOwnProperty-Methode verwenden:

for (var prop in obj) {
  if (obj.hasOwnProperty(prop)) {
    // prop ist nicht geerbt
  }
}

Und einige empfehlen sogar, die Methode direkt von Object.prototype aufzurufen, um Probleme zu vermeiden, falls jemand eine Eigenschaft namens hasOwnProperty zu unserem Objekt hinzufügt:

for (var prop in obj) {
  if (Object.prototype.hasOwnProperty.call(obj, prop)) {
    // prop ist nicht geerbt
  }
}

10 Stimmen

Siehe auch den Beitrag von David Humphrey Iterating over Objects in JavaScript Quickly - für Arrays ist for..in viel langsamer als "normale" Schleifen.

18 Stimmen

Frage zum letzten Punkt über "hasOwnProperty": Wenn jemand "hasOwnProperty" auf einem Objekt überschreibt, bekommst du Probleme. Aber wirst du nicht die gleichen Probleme haben, wenn jemand "Object.prototype.hasOwnProperty" überschreibt? So oder so bringen sie dich durcheinander und es ist nicht deine Verantwortung, richtig?

0 Stimmen

Du sagst for..in ist keine schlechte Praxis, aber es kann missbraucht werden. Hast du ein echtes Beispiel aus der Praxis, wo du wirklich alle Eigenschaften eines Objekts durchlaufen möchtest, einschließlich der geerbten Eigenschaften?

130voto

Christoph Punkte 157217

Es gibt drei Gründe, warum Sie for..in nicht verwenden sollten, um über Array-Elemente zu iterieren:

  • for..in wird über alle eigenen und geerbten Eigenschaften des Array-Objekts iterieren, die nicht DontEnum sind; das bedeutet, dass wenn jemand Eigenschaften zum spezifischen Array-Objekt hinzufügt (es gibt gültige Gründe dafür - ich habe das auch gemacht) oder Array.prototype geändert wird (was als schlechte Praxis in Code gilt, der gut mit anderen Skripten zusammenarbeiten soll), diese Eigenschaften ebenfalls durchlaufen werden; geerbte Eigenschaften können durch Überprüfung von hasOwnProperty() ausgeschlossen werden, aber das hilft nicht bei Eigenschaften, die im Array-Objekt selbst festgelegt sind

  • for..in garantiert nicht die Beibehaltung der Elementreihenfolge

  • Es ist langsam, weil Sie alle Eigenschaften des Array-Objekts und seiner gesamten Prototypenkette durchlaufen müssen und trotzdem nur den Namen der Eigenschaft erhalten, d. h. um den Wert zu erhalten, ist eine zusätzliche Suche erforderlich

61voto

Pim Jager Punkte 31389

Weil bei for...in das Objekt durchlaufen wird, das das Array enthält, nicht das Array selbst. Wenn ich eine Funktion in die Prototypenkette des Arrays hinzufüge, wird diese ebenfalls enthalten sein. Z.B.

Array.prototype.myOwnFunction = function() { alert(this); }
a = new Array();
a[0] = 'foo';
a[1] = 'bar';
for (var x in a) {
    document.write(x + ' = ' + a[x]);
}

Dies wird geschrieben:

0 = foo
1 = bar
myOwnFunction = function() { alert(this); }

Und da man nie sicher sein kann, dass nichts zur Prototypenkette hinzugefügt wird, verwenden Sie einfach eine for-Schleife, um das Array aufzuzählen:

for (var i=0,x=a.length; i

0 Stimmen

Also, eine bessere Aussage in diesem Fall lautet: "Verwende kein for-in bei Arrays, weil es alles einschließen wird, was du dem Prototypen hinzufügst"?

58voto

MarcG Punkte 25031

Ab 2016 (ES6) können wir for ... of zur Array-Iteration verwenden, wie bereits von John Slegers festgestellt.

Ich möchte einfach diesen einfachen Demonstrationscode hinzufügen, um die Dinge klarer zu machen:

Array.prototype.foo = 1;
var arr = [];
arr[5] = "xyz";

console.log("for...of:");
var count = 0;
for (var item of arr) {
    console.log(count + ":", item);
    count++;
}

console.log("for...in:");
count = 0;
for (var item in arr) {
    console.log(count + ":", item);
    count++;
}

Die Konsole zeigt:

for...of:

0: undefined
1: undefined
2: undefined
3: undefined
4: undefined
5: xyz

for...in:

0: 5
1: foo

Mit anderen Worten:

  • for...of zählt von 0 bis 5 und ignoriert auch Array.prototype.foo. Es zeigt Array Werte.

  • for...in listet nur die 5 auf, ignoriert die undefinierten Array-Indizes, fügt aber foo hinzu. Es zeigt Array Eigenschaftsnamen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X