2083 Stimmen

Warum ist es eine schlechte Idee, "for...in" zur Iteration von Arrays zu verwenden?

Man hat mir gesagt, for...in nicht mit Arrays in JavaScript zu verwenden. Warum nicht?

54 Stimmen

Ich habe die kürzliche Frage gesehen, in der jemand das zu dir gesagt hat, aber sie meinten nur für Arrays. Es wird als schlechte Praxis betrachtet, 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ärtsiteration. Gibt es eine andere Version der Vorwärtsiteration, die schneller ist?

1735voto

Kenan Banks Punkte 196831

Der Grund dafür ist, dass ein Konstrukt:

var a = []; // Erstelle ein neues leeres Array.
a[5] = 5;   // Völlig legales JavaScript, das das Array neu dimensioniert.

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

/* Wird angezeigt:
   undefined
   undefined
   undefined
   undefined
   undefined
   5
*/

manchmal völlig anders sein kann 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 angezeigt:
   5
*/

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

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

// Jetzt wissen Sie nicht, was der nachstehende Code tun wird.
var a = [1, 2, 3, 4, 5];
for (var x in a){
    // Jetzt ist foo ein Bestandteil JEDES Arrays und
    // wird hier als Wert von 'x' erscheinen.
    console.log(x);
}

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

428 Stimmen

Erinnere dich daran, (var x in a) anstelle von (x in a) zu verwenden - möchte keine globale Variable erstellen.

94 Stimmen

Die erste Frage ist kein Grund, warum es schlecht ist, sondern nur ein Unterschied in der Semantik. Das zweite Problem erscheint mir als ein Grund (zusätzlich zu Konflikten zwischen Bibliotheken, die dasselbe tun), warum es schlecht ist, das Prototyp eines integrierten Datentyps zu ändern, 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 das ist der Grund, warum die Konstruktion in der Regel vermieden wird.

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.

Zweck der for-in-Anweisung ist es, über Objekteigenschaften zu iterieren. Diese Anweisung wird sich in der Prototypenkette bewegen und auch über vererbte Eigenschaften iterieren, was manchmal nicht erwünscht ist.

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

Zum Beispiel ist in JScript (IE <= 8) die Reihenfolge der Auflistung selbst bei Array-Objekten so definiert, wie die Eigenschaften erstellt wurden:

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

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

Außerdem, wenn es um vererbte Eigenschaften geht, werden diese auch aufgelistet, zum Beispiel wenn du das Array.prototype Objekt erweiterst (wie einige Bibliotheken wie MooTools es tun):

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

for (var p in []) { // ein leeres Array
  // last wird aufgelistet
}

Wie schon erwähnt, 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 du nur die eigenen Eigenschaften eines Objekts auflisten möchtest (die nicht vererbt wurden), kannst du die hasOwnProperty Methode verwenden:

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

Und manche empfehlen sogar, die Methode direkt aus Object.prototype aufzurufen, um Probleme zu vermeiden, wenn 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 vererbt
  }
}

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 zu "hasOwnProperty": Wenn jemand "hasOwnProperty" auf einem Objekt überschreibt, wirst du Probleme haben. Aber hast du nicht die gleichen Probleme, wenn jemand "Object.prototype.hasOwnProperty" überschreibt? So oder so bringen sie dich durcheinander und es ist nicht deine Verantwortung, oder?

0 Stimmen

Du sagst, dass for..in keine schlechte Praxis ist, aber es kann missbraucht werden. Hast du ein reales Beispiel für gute Praxis, bei dem du wirklich alle Eigenschaften eines Objekts durchgehen wolltest, einschließlich der vererbten 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, wenn jemand Eigenschaften zum spezifischen Array-Objekt hinzufügt (es gibt gültige Gründe dafür - ich habe es selbst getan) oder Array.prototype geändert hat (was als schlechte Praxis in Code gilt, der gut mit anderen Skripten zusammenarbeiten soll), werden diese Eigenschaften ebenfalls durchlaufen; geerbte Eigenschaften können durch Überprüfen von hasOwnProperty() ausgeschlossen werden, aber das hilft Ihnen nicht bei Eigenschaften, die im Array-Objekt selbst festgelegt wurden

  • for..in garantiert nicht die Erhaltung 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; um den Wert zu erhalten, ist eine zusätzliche Nachschlage erforderlich

61voto

Pim Jager Punkte 31389

Weil for...in durch das Objekt durchläuft, das das Array hält, nicht durch das Array selbst. Wenn ich eine Funktion zur Prototypenkette der Arrays hinzufüge, wird diese auch eingeschlossen. d. h.

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 schreibt:

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 aufzulisten:

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

0 Stimmen

So eine bessere Aussage in diesem Fall wäre: "Verwenden Sie kein for-in für Arrays, weil es alles einschließen wird, was Sie dem Prototypen hinzufügen"?

58voto

MarcG Punkte 25031

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

Ich möchte nur 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 undefinierte 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