Man hat mir gesagt, for...in
nicht mit Arrays in JavaScript zu verwenden. Warum nicht?
Erinnere dich daran, (var x in a)
anstelle von (x in a)
zu verwenden - möchte keine globale Variable erstellen.
Man hat mir gesagt, for...in
nicht mit Arrays in JavaScript zu verwenden. Warum nicht?
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
*/
Erinnere dich daran, (var x in a)
anstelle von (x in a)
zu verwenden - möchte keine globale Variable erstellen.
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.
@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.
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
}
}
Siehe auch den Beitrag von David Humphrey Iterating over Objects in JavaScript Quickly - für Arrays ist for..in
viel langsamer als "normale" Schleifen.
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?
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?
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
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
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 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.
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?
1 Stimmen
@MattDiPasquale - Ja, es ist umgekehrt, jedoch unter einer bekannten Grenze ermöglicht es eine optimale Verarbeitung, insbesondere in älteren Browsern, durch Iteration über einen bekannten Satz im Gegensatz zu einer unbekannten Satzlänge, indem die Grenze zuerst festgelegt wird. Es ist die Natur einer interpretierten Sprache.
0 Stimmen
@MarkSchultheiss Guter Kommentar. Man könnte die
for
-Schleife auch alsfor (var i = hColl.length; i--;) {}
schreiben, was ein ähnliches Leistungsprofil wie die umgekehrtewhile
-Schleife haben sollte.5 Stimmen
@Wynand verwenden Sie
var i = hCol1.length; for (i;i;i--;) {}
speichern Sie das i im Cache, da es einen Unterschied macht und den Test vereinfacht. - je älter der Browser, desto größer der Unterschied zwischenfor
undwhile
IMMER den "i" Zähler im Cache speichern - und natürlich passt negativ nicht immer zur Situation und das Negative, währendobfuscate
den Code für einige Leute ein wenig verkompliziert. und beachten Sievar i = 1000; for (i; i; i--) {}
undvar b =1000 for (b; b--;) {}
wobei i von 1000 auf 1 und b von 999 auf 0 geht. - je älter der Browser, desto mehr neigt das while zur Leistungssteigerung.12 Stimmen
Sie können auch klug sein.
for(var i = 0, l = myArray.length; i < l; ++i) ...
ist die schnellste und beste Möglichkeit, die Sie mit einem Vorwärtsdurchlauf erhalten können.0 Stimmen
Es ist jetzt 10 Jahre später und ich verspüre den Drang hinzuzufügen: Auch wenn diese Optimierungen den Code leicht schneller machen: es sind Mikro-Optimierungen. Wenn Sie sich unsicher sind, entscheiden Sie sich für lesbaren Code, nicht für eine stark optimierte Schleife über ein Array mit 1000 Elementen. Wenn etwas langsam ist, dann ist es normalerweise der Code, der innerhalb der Schleife ausgeführt wird, nicht die Schleifensteuerung.