Warum ist die Verwendung von "for ... in" mit Array-Iteration eine schlechte Idee?

1823

Mir wurde gesagt, dass ich keine for...inArrays in JavaScript verwenden soll. Warum nicht?

lYriCAlsSH
quelle
45
Ich habe die letzte Frage gesehen, bei der jemand das zu Ihnen gesagt hat, aber sie waren nur für Arrays gedacht. Es wird als schlechte Praxis angesehen, Arrays zu durchlaufen, aber nicht unbedingt, Elemente eines Objekts zu durchlaufen.
mmurch
19
Viele Antworten mit "for" -Schleifen wie 'for (var i = 0; i <hColl.length; i ++) {}' vergleichen mit 'var i = hColl.length; während (i--) {} ', was, wenn es möglich ist, die letztere Form zu verwenden, wesentlich schneller ist. Ich weiß, dass dies tangential ist, dachte aber, ich würde dieses Bit hinzufügen.
Mark Schultheiss
2
@ MarkSchultheiss, aber das ist umgekehrte Iteration. Gibt es eine andere Version der Vorwärtsiteration, die schneller ist?
ma11hew28
5
@Wynand Verwenden Sie den var i = hCol1.length; for (i;i;i--;) {}Cache des i, da dies einen Unterschied macht, und vereinfachen Sie den Test. - Je älter der Browser ist, desto größer ist der Unterschied zwischen forund whileIMMER im Cache des "i" -Zählers - und natürlich passt das Negative nicht immer zur Situation und das Negative, während obfuscate der Code für manche Leute ein bisschen ist. und beachten Sie, var i = 1000; for (i; i; i--) {}und var b =1000 for (b; b--;) {}wo ich 1000 zu 1 und b 999 zu 0 gehe. - Je älter der Browser, desto mehr wird die Leistung tendenziell begünstigt.
Mark Schultheiss
9
Sie können auch klug sein. for(var i = 0, l = myArray.length; i < l; ++i) ...ist die schnellste und beste, die Sie mit Vorwärtsiteration erhalten können.
Mathieu Amiot

Antworten:

1557

Der Grund ist, dass ein Konstrukt:

var a = []; // Create a new empty array.
a[5] = 5;   // Perfectly legal JavaScript that resizes the array.

for (var i = 0; i < a.length; i++) {
    // Iterate over numeric indexes from 0 to 5, as everyone expects.
    console.log(a[i]);
}

/* Will display:
   undefined
   undefined
   undefined
   undefined
   undefined
   5
*/

kann manchmal ganz anders sein als die anderen:

var a = [];
a[5] = 5;
for (var x in a) {
    // Shows only the explicitly set index of "5", and ignores 0-4
    console.log(x);
}

/* Will display:
   5
*/

Bedenken Sie auch, dass JavaScript- Bibliotheken solche Aktionen ausführen können, die sich auf jedes von Ihnen erstellte Array auswirken:

// Somewhere deep in your JavaScript library...
Array.prototype.foo = 1;

// Now you have no idea what the below code will do.
var a = [1, 2, 3, 4, 5];
for (var x in a){
    // Now foo is a part of EVERY array and 
    // will show up here as a value of 'x'.
    console.log(x);
}

/* Will display:
   0
   1
   2
   3
   4
   foo
*/

Triptychon
quelle
146
In der Vergangenheit haben einige Browser sogar "Länge", "toString" usw. durchlaufen!
Bobince
398
Denken Sie daran, zu verwenden, (var x in a)anstatt (x in a)- keine globale zu erstellen.
Chris Morgan
78
Die erste Ausgabe ist kein Grund, warum sie schlecht ist, sondern nur ein Unterschied in der Semantik. Das zweite Problem scheint mir ein Grund zu sein (zusätzlich zu den Konflikten zwischen Bibliotheken, die dasselbe tun), dass das Ändern des Prototyps eines integrierten Datentyps schlecht ist, anstatt dass das für ... in schlecht ist.
Stewart
86
@Stewart: Alle Objekte in JS sind assoziativ. Ein JS-Array ist ein Objekt, also ja, es ist auch assoziativ, aber dafür ist es nicht gedacht. Wenn Sie die Schlüssel eines Objekts durchlaufen möchten , verwenden Sie for (var key in object). Wenn Sie jedoch über die Elemente eines Arrays iterieren möchten , verwenden Sie for(var i = 0; i < array.length; i += 1).
Martijn
42
Sie sagten für das erste Beispiel, dass es über numerische Indizes von 0 bis 4 iteriert , wie jeder erwartet , ich erwarte , dass es von 0 bis 5 iteriert ! Wenn Sie an Position 5 ein Element hinzufügen, enthält das Array 6 Elemente (davon 5 undefiniert).
Stivlo
393

Die for-inAnweisung an sich ist keine "schlechte Praxis", kann jedoch missbraucht werden , um beispielsweise über Arrays oder Array-ähnliche Objekte zu iterieren .

Der Zweck der for-inErklärung ist aufzuzählen Objekteigenschaften über. Diese Aussage wird in der Prototypenkette auftauchen und auch geerbte Eigenschaften aufzählen , was manchmal nicht erwünscht ist.

Außerdem wird die Reihenfolge der Iteration nicht durch die Spezifikation garantiert. Wenn Sie also ein Array-Objekt "iterieren" möchten, können Sie mit dieser Anweisung nicht sicher sein, dass die Eigenschaften (Array-Indizes) in der numerischen Reihenfolge aufgerufen werden.

In JScript (IE <= 8) wird beispielsweise die Reihenfolge der Aufzählung auch für Array-Objekte definiert, wenn die Eigenschaften erstellt wurden:

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

for (var p in array) {
  //... p will be "2", "1" and "0" on IE
}

Wenn Sie beispielsweise über geerbte Eigenschaften sprechen und das Array.prototypeObjekt erweitern (wie dies bei einigen Bibliotheken wie bei MooTools der Fall ist), werden diese Eigenschaften ebenfalls aufgelistet:

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

for (var p in []) { // an empty array
  // last will be enumerated
}

Wie ich bereits sagte, um Arrays oder Array-ähnliche Objekte zu durchlaufen , ist es am besten, eine sequentielle Schleife zu verwenden , z. B. eine Plain-Old for/ while-Schleife.

Wenn Sie nur die eigenen Eigenschaften eines Objekts auflisten möchten (diejenigen, die nicht vererbt wurden), können Sie die folgende hasOwnPropertyMethode verwenden:

for (var prop in obj) {
  if (obj.hasOwnProperty(prop)) {
    // prop is not inherited
  }
}

Einige Leute empfehlen sogar, die Methode direkt von aufzurufen Object.prototype, um Probleme zu vermeiden, wenn jemand hasOwnPropertyunserem Objekt eine Eigenschaft mit dem Namen hinzufügt :

for (var prop in obj) {
  if (Object.prototype.hasOwnProperty.call(obj, prop)) {
    // prop is not inherited
  }
}
CMS
quelle
10
Siehe auch David Humphreys Beitrag Iterieren über Objekte in JavaScript Schnell - denn Arrays for..insind viel langsamer als "normale" Schleifen.
Chris Morgan
17
Frage zum letzten Punkt zu "hasOwnProperty": Wenn jemand "hasOwnProperty" für ein Objekt überschreibt, treten Probleme auf. Aber haben Sie nicht die gleichen Probleme, wenn jemand "Object.prototype.hasOwnProperty" überschreibt? Wie auch immer, sie vermasseln dich und es liegt nicht in deiner Verantwortung, oder?
Scott Rippey
Sie sagen, es for..inist keine schlechte Praxis, aber es kann missbraucht werden. Haben Sie ein Beispiel für eine gute Praxis in der Praxis, bei dem Sie wirklich alle Objekteigenschaften einschließlich der geerbten Eigenschaften durchgehen wollten?
rjmunro
4
@ ScottRippey: Wenn du es dorthin bringen willst: youtube.com/watch?v=FrFUI591WhI
Nathan Wall
Mit dieser Antwort fand ich, dass mitfor (var p in array) { array[p]; }
equiman
117

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

  • for..inwird alle eigenen und geerbten Eigenschaften des Array-Objekts durchlaufen, die es nicht sind DontEnum; Das bedeutet, wenn jemand dem spezifischen Array-Objekt Eigenschaften hinzufügt (es gibt gültige Gründe dafür - ich habe es selbst getan) oder geändert hat Array.prototype(was als schlechte Praxis in Code angesehen wird, der mit anderen Skripten gut funktionieren soll), werden diese Eigenschaften auch wiederholt werden; geerbte Eigenschaften können durch Überprüfen ausgeschlossen werden hasOwnProperty(), dies hilft Ihnen jedoch nicht bei den im Array-Objekt selbst festgelegten Eigenschaften

  • for..in Es wird nicht garantiert, dass die Elementreihenfolge erhalten bleibt

  • Es ist langsam, da Sie alle Eigenschaften des Array-Objekts und seiner gesamten Prototypenkette durchlaufen müssen und immer noch nur den Namen der Eigenschaft erhalten, dh um den Wert zu erhalten, ist eine zusätzliche Suche erforderlich

Christoph
quelle
55

Denn for ... in zählt durch das Objekt auf, das das Array enthält, nicht das Array selbst. Wenn ich der Prototypkette des Arrays eine Funktion hinzufüge, wird diese ebenfalls berücksichtigt. Dh

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

Dies wird schreiben:

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

Und da Sie nie sicher sein können, dass der Prototypenkette nichts hinzugefügt wird, verwenden Sie einfach eine for-Schleife, um das Array aufzulisten:

for(i=0,x=a.length;i<x;i++){
 document.write(i + ' = ' + a[i]);
}

Dies wird schreiben:

0 = foo
1 = bar
Pim Jäger
quelle
16
Arrays sind Objekte, es gibt kein "Objekt, das das Array enthält".
RobG
41

Für sich genommen ist die Verwendung von For-In für Arrays nichts Falsches. For-in iteriert über die Eigenschaftsnamen eines Objekts, und im Fall eines "Out-of-the-Box" -Arrays entsprechen die Eigenschaften den Array-Indizes. (Der eingebaute in propertes wie length, toStringund so weiter sind nicht in der Iteration enthalten.)

Wenn Ihr Code (oder das von Ihnen verwendete Framework) Arrays oder dem Array-Prototyp benutzerdefinierte Eigenschaften hinzufügt, werden diese Eigenschaften in die Iteration einbezogen, was wahrscheinlich nicht Ihren Wünschen entspricht.

Einige JS-Frameworks wie Prototype ändern den Array-Prototyp. Andere Frameworks wie JQuery tun dies nicht. Mit JQuery können Sie For-In sicher verwenden.

Wenn Sie Zweifel haben, sollten Sie For-In wahrscheinlich nicht verwenden.

Eine alternative Methode zum Durchlaufen eines Arrays ist die Verwendung einer for-Schleife:

for (var ix=0;ix<arr.length;ix++) alert(ix);

Dies hat jedoch ein anderes Problem. Das Problem ist, dass ein JavaScript-Array "Löcher" haben kann. Wenn Sie definieren arrals:

var arr = ["hello"];
arr[100] = "goodbye";

Dann hat das Array zwei Elemente, aber eine Länge von 101. Die Verwendung von for-in ergibt zwei Indizes, während die for-Schleife 101 Indizes ergibt, wobei die 99 einen Wert von hat undefined.

JacquesB
quelle
37

Ab 2016 (ES6) können wir for…offür die Array-Iteration verwenden, wie John Slegers bereits bemerkt hat.

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...ofzählt von 0 bis 5 und ignoriert auch Array.prototype.foo. Es zeigt Array- Werte .

  • for...inlistet nur die 5undefinierten Array-Indizes auf, ignoriert sie jedoch foo. Es zeigt Namen der Array- Eigenschaften .

MarcG
quelle
32

Kurze Antwort: Es lohnt sich einfach nicht.


Längere Antwort: Es lohnt sich einfach nicht, auch wenn keine sequentielle Elementreihenfolge und keine optimale Leistung erforderlich sind.


Lange Antwort: Es lohnt sich einfach nicht ...

  • Die Verwendung for (var property in array)wird dazu führen array, dass sie als Objekt durchlaufen wird, die Objektprototypkette durchläuft und letztendlich langsamer als eine indexbasierte forSchleife ist.
  • for (... in ...) Es ist nicht garantiert, dass die Objekteigenschaften wie erwartet in sequentieller Reihenfolge zurückgegeben werden.
  • Das Verwenden hasOwnProperty()und !isNaN()Überprüfen zum Filtern der Objekteigenschaften ist ein zusätzlicher Aufwand, der zu einer noch langsameren Leistung führt und den Hauptgrund für die erstmalige Verwendung negiert, dh aufgrund des präziseren Formats.

Aus diesen Gründen besteht nicht einmal ein akzeptabler Kompromiss zwischen Leistung und Komfort. Es gibt wirklich keinen Vorteil, es sei denn, die Absicht besteht darin, das Array als Objekt zu behandeln und Operationen an den Objekteigenschaften des Arrays auszuführen.

WynandB
quelle
31

Zusätzlich zu den in anderen Antworten angegebenen Gründen möchten Sie möglicherweise nicht die Struktur "for ... in" verwenden, wenn Sie mit der Zählervariablen rechnen müssen, da die Schleife die Namen der Objekteigenschaften und damit der Variablen durchläuft ist eine Zeichenfolge.

Zum Beispiel,

for (var i=0; i<a.length; i++) {
    document.write(i + ', ' + typeof i + ', ' + i+1);
}

werde schreiben

0, number, 1
1, number, 2
...

wohingegen,

for (var ii in a) {
    document.write(i + ', ' + typeof i + ', ' + i+1);
}

werde schreiben

0, string, 01
1, string, 11
...

Dies kann natürlich leicht durch Einbeziehen überwunden werden

ii = parseInt(ii);

in der Schleife, aber die erste Struktur ist direkter.

ctmiddle
quelle
6
Sie können +stattdessen das Präfix verwenden, es parseIntsei denn, Sie benötigen wirklich eine Ganzzahl oder ignorieren ungültige Zeichen.
Konrad Borowski
Die Verwendung parseInt()wird ebenfalls nicht empfohlen. Versuchen Sie es parseInt("025");und es wird fehlschlagen.
Derek 5 會 功夫
6
@Derek 朕 會 功夫 - Sie können definitiv verwenden parseInt. Das Problem ist, wenn Sie den Radix nicht einschließen, versuchen ältere Browser möglicherweise, die Zahl zu interpretieren (daher wird 025 oktal). Dies wurde in ECMAScript 5 behoben, aber es passiert immer noch für Zahlen, die mit "0x" beginnen (es interpretiert die Zahl als hexadezimal). Um auf der sicheren Seite zu sein, verwenden Sie den Radix, um die Nummer wie parseInt("025", 10)
folgt
23

Abgesehen von der Tatsache, dass for... inalle aufzählbaren Eigenschaften durchläuft (was nicht mit "allen Array-Elementen" identisch ist!), Siehe http://www.ecma-international.org/publications/files/ECMA-ST/Ecma -262.pdf , Abschnitt 12.6.4 (5. Ausgabe) oder 13.7.5.15 (7. Ausgabe):

Die Mechanik und Reihenfolge der Aufzählung der Eigenschaften ... ist nicht festgelegt ...

(Hervorhebung von mir.)

Das heißt, wenn ein Browser dies wollte, konnte er die Eigenschaften in der Reihenfolge durchlaufen, in der sie eingefügt wurden. Oder in numerischer Reihenfolge. Oder in lexikalischer Reihenfolge (wobei "30" vor "4" steht! Beachten Sie, dass alle Objektschlüssel - und damit alle Array-Indizes - tatsächlich Zeichenfolgen sind, was absolut sinnvoll ist). Es könnte sie per Bucket durchlaufen, wenn es Objekte als Hash-Tabellen implementiert. Oder nehmen Sie etwas davon und fügen Sie "rückwärts" hinzu. Ein Browser kann sogar nach dem Zufallsprinzip iterieren und ECMA-262-konform sein, sofern er jede Eigenschaft genau einmal besucht.

In der Praxis iterieren die meisten Browser derzeit gerne in ungefähr derselben Reihenfolge. Aber es gibt nichts zu sagen, dass sie müssen. Das ist implementierungsspezifisch und kann sich jederzeit ändern, wenn sich ein anderer Weg als weitaus effizienter herausstellt.

Wie auch immer, for... inbringt keine Konnotation der Ordnung mit sich. Wenn Sie sich für die Reihenfolge interessieren, geben Sie dies explizit an und verwenden Sie eine reguläre forSchleife mit einem Index.

cHao
quelle
18

Hauptsächlich zwei Gründe:

Eins

Wie andere gesagt haben, erhalten Sie möglicherweise Schlüssel, die nicht in Ihrem Array enthalten sind oder die vom Prototyp geerbt wurden. Wenn also beispielsweise eine Bibliothek den Array- oder Objektprototypen eine Eigenschaft hinzufügt:

Array.prototype.someProperty = true

Sie erhalten es als Teil jedes Arrays:

for(var item in [1,2,3]){
  console.log(item) // will log 1,2,3 but also "someProperty"
}

Sie können dies mit der hasOwnProperty-Methode lösen:

var ary = [1,2,3];
for(var item in ary){
   if(ary.hasOwnProperty(item)){
      console.log(item) // will log only 1,2,3
   }
}

Dies gilt jedoch für das Durchlaufen eines Objekts mit einer For-In-Schleife.

Zwei

Normalerweise ist die Reihenfolge der Elemente in einem Array wichtig, aber die For-In-Schleife wird nicht unbedingt in der richtigen Reihenfolge durchlaufen, da das Array als Objekt behandelt wird, wie es in JS implementiert ist, und nicht als Array. Dies scheint eine kleine Sache zu sein, aber es kann Anwendungen wirklich vermasseln und ist schwer zu debuggen.

Lior
quelle
2
Object.keys(a).forEach( function(item) { console.log(item) } )Durchlaufen Sie ein Array eigener Eigenschaftsschlüssel, nicht die vom Prototyp geerbten.
Qwerty
2
Richtig, aber wie die For-In-Schleife muss sie nicht unbedingt in der richtigen Indexreihenfolge vorliegen. Außerdem funktioniert es nicht mit älteren Browsern, die ES5 nicht unterstützen.
Lior
Sie können diese Browser unterrichten, array.forEachindem Sie bestimmten Code in Ihre Skripte einfügen. Siehe Polyfill developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Qwerty
Natürlich, aber dann manipulieren Sie den Prototyp, und das ist nicht immer eine gute Idee ... und trotzdem haben Sie das Problem der Bestellung ...
Lior
Und natürlich Grund Nummer drei: Sparse Arrays.
Ein besserer Oliver
16

Weil es durch Objektfelder und nicht durch Indizes auflistet. Sie können einen Wert mit dem Index "Länge" erhalten, und ich bezweifle, dass Sie dies möchten.

vava
quelle
Was ist der beste Weg, das zu tun?
LyriCAlsSH
3
für (var i = 0; i <arr.length; i ++) {}
vava
3
In Firefox 3 können Sie auch entweder arr.forEach oder for (var [i, v] in Iterator (arr)) {} verwenden, aber keines davon funktioniert im IE, obwohl Sie forEach-Methode selbst schreiben können.
Vava
und praktisch jede Bibliothek hat auch dafür ihre eigene Methode.
Vava
5
Diese Antwort ist falsch. "Länge" wird nicht in die For-In-Iteration einbezogen. Es sind nur Eigenschaften enthalten, die Sie selbst hinzufügen.
JacquesB
16

Ich glaube nicht, dass ich z. Die Antwort von Triptychon oder CMS, warum die Verwendung for...inin einigen Fällen vermieden werden sollte.

Ich möchte jedoch hinzufügen, dass es in modernen Browsern eine Alternative for...ingibt, die in den Fällen verwendet werden kann, in denen for...insie nicht verwendet werden können. Diese Alternative ist for...of:

for (var item of items) {
    console.log(item);
}

Hinweis :

Leider unterstützt keine Version von Internet Explorer for...of( Edge 12+ ), sodass Sie etwas länger warten müssen, bis Sie sie in Ihrem clientseitigen Produktionscode verwenden können. Die Verwendung in Ihrem serverseitigen JS-Code sollte jedoch sicher sein (wenn Sie Node.js verwenden ).

John Slegers
quelle
@georgeawg Du meintest for-of, nicht wahr for-in?
4 ᆺ ᆼ
15

Das Problem mit for ... in ...- und dies wird nur dann zu einem Problem, wenn ein Programmierer die Sprache nicht wirklich versteht. Es ist nicht wirklich ein Fehler oder so etwas - es ist, dass es über alle Mitglieder eines Objekts iteriert (nun, alle aufzählbaren Mitglieder, aber das ist ein Detail für den Moment). Wenn Sie nur die indizierten Eigenschaften eines Arrays durchlaufen möchten , besteht die einzige garantierte Möglichkeit, die Dinge semantisch konsistent zu halten, darin, einen ganzzahligen Index (dh eine for (var i = 0; i < array.length; ++i)Stilschleife) zu verwenden.

Jedem Objekt können beliebige Eigenschaften zugeordnet werden. Insbesondere das Laden zusätzlicher Eigenschaften auf eine Array-Instanz wäre nicht schrecklich. Code, der nur indizierte Array-ähnliche Eigenschaften sehen möchte, muss sich daher an einen ganzzahligen Index halten. Code, der genau weiß, was alle Eigenschaften for ... intut und wirklich braucht , dann ist das auch in Ordnung.

Spitze
quelle
Schöne Erklärung Pointy. Nur neugierig. Wenn ich ein Array hätte, das sich innerhalb eines Objekts unter Multiplikationseigenschaften befindet, und for indiese Arrays im Vergleich zu einer regulären for-Schleife durchlaufen würden? (
Was
2
@ NiCkNewman gut das Objekt, nach dem Sie inin einer for ... inSchleife referenzieren , wird nur
Pointy
Aha. Nur neugierig, weil ich Objekte und Arrays in meinem Hauptspielobjekt habe und mich gefragt habe, ob das Inning schmerzhafter wäre als nur eine reguläre for-Schleife in den Indizes.
NiCk Newman
@NiCkNewman Nun, das Thema dieser ganzen Frage ist, dass Sie es einfach nicht for ... infür Arrays verwenden sollten. Es gibt viele gute Gründe, dies nicht zu tun. Es ist weniger ein Leistungsproblem als vielmehr ein Problem, bei dem sichergestellt wird, dass es nicht kaputt geht.
Pointy
Nun, meine Objekte sind technisch in einem Array gespeichert, deshalb habe ich mir Sorgen gemacht, so etwas wie: [{a:'hey',b:'hi'},{a:'hey',b:'hi'}]Aber ja, ich verstehe.
NiCk Newman
9

Aufgrund der Semantik ist die Art und for, inWeise, wie Arrays behandelt werden (dh wie jedes andere JavaScript-Objekt), nicht mit anderen gängigen Sprachen ausgerichtet.

// C#
char[] a = new char[] {'A', 'B', 'C'};
foreach (char x in a) System.Console.Write(x); //Output: "ABC"

// Java
char[] a = {'A', 'B', 'C'};
for (char x : a) System.out.print(x);          //Output: "ABC"

// PHP
$a = array('A', 'B', 'C');
foreach ($a as $x) echo $x;                    //Output: "ABC"

// JavaScript
var a = ['A', 'B', 'C'];
for (var x in a) document.write(x);            //Output: "012"
Matpop
quelle
9

TL & DR: Die Verwendung der for inSchleife in Arrays ist nicht böse, im Gegenteil.

Ich denke, die for inSchleife ist ein Juwel von JS, wenn sie in Arrays richtig verwendet wird . Es wird erwartet, dass Sie die volle Kontrolle über Ihre Software haben und wissen, was Sie tun. Lassen Sie uns die genannten Nachteile sehen und sie nacheinander widerlegen.

  1. Es durchläuft auch geerbte Eigenschaften: Zuallererst sollten alle Erweiterungen Array.prototypevon mit using vorgenommen worden sein Object.defineProperty()und ihr enumerableDeskriptor sollte auf gesetzt sein false. Eine Bibliothek, die dies nicht tut, sollte überhaupt nicht verwendet werden.
  2. Eigenschaften, die Sie später zur Vererbungskette hinzufügen, werden gezählt: Wenn Sie eine Array-Unterklassifizierung nach Object.setPrototypeOfoder nach Klasse durchführen extend. Verwenden Sie sollten wieder Object.defineProperty()die standardmäßig setzt den writable, enumerableund configurableEigenschaftendeskriptoren zu false. Sehen wir uns hier ein Beispiel für eine Unterklassifizierung von Arrays an ...

function Stack(...a){
  var stack = new Array(...a);
  Object.setPrototypeOf(stack, Stack.prototype);
  return stack;
}
Stack.prototype = Object.create(Array.prototype);                                 // now stack has full access to array methods.
Object.defineProperty(Stack.prototype,"constructor",{value:Stack});               // now Stack is a proper constructor
Object.defineProperty(Stack.prototype,"peak",{value: function(){                  // add Stack "only" methods to the Stack.prototype.
                                                       return this[this.length-1];
                                                     }
                                             });
var s = new Stack(1,2,3,4,1);
console.log(s.peak());
s[s.length] = 7;
console.log("length:",s.length);
s.push(42);
console.log(JSON.stringify(s));
console.log("length:",s.length);

for(var i in s) console.log(s[i]);

Sie sehen also, die for inSchleife ist jetzt sicher, da Sie sich um Ihren Code gekümmert haben.

  1. Die for inSchleife ist langsam: Hölle nein. Dies ist bei weitem die schnellste Methode zur Iteration, wenn Sie über spärliche Arrays laufen, die von Zeit zu Zeit benötigt werden. Dies ist einer der wichtigsten Performance-Tricks, die man kennen sollte. Sehen wir uns ein Beispiel an. Wir werden ein spärliches Array durchlaufen.

var a = [];
a[0] = "zero";
a[10000000] = "ten million";
console.time("for loop on array a:");
for(var i=0; i < a.length; i++) a[i] && console.log(a[i]);
console.timeEnd("for loop on array a:");
console.time("for in loop on array a:");
for(var i in a) a[i] && console.log(a[i]);
console.timeEnd("for in loop on array a:");

Reduzieren
quelle
@ Ravi Shanker Reddy Gutes Benchmarking eingerichtet. Wie ich in meiner Antwort erwähnt habe, for inüberstrahlt die Schleife die anderen "wenn" das Array spärlich ist und je größer es wird, desto größer wird es. Also habe ich den Bench-Test für ein spärliches Array mit arreiner Größe von ~ 10000 neu angeordnet, wobei nur 50 Elemente zufällig aus [42,"test",{t:1},null, void 0]zufälligen Indizes ausgewählt wurden. Sie werden den Unterschied sofort bemerken. - >> Hier nachschauen << - .
Reduzieren Sie den
8

Zusätzlich zu den anderen Problemen ist die Syntax "for..in" wahrscheinlich langsamer, da der Index eine Zeichenfolge und keine Ganzzahl ist.

var a = ["a"]
for (var i in a)
    alert(typeof i)  // 'string'
for (var i = 0; i < a.length; i++)
    alert(typeof i)  // 'number'
dc1
quelle
Wahrscheinlich spielt das keine große Rolle. Array-Elemente sind Eigenschaften eines Array-basierten oder Array-ähnlichen Objekts, und alle Objekteigenschaften verfügen über Zeichenfolgenschlüssel. Wenn Ihre JS-Engine es nicht irgendwie optimiert, wird es, selbst wenn Sie eine Zahl verwenden, in eine Zeichenfolge für die Suche umgewandelt.
CHao
Unabhängig von Leistungsproblemen: Wenn Sie mit JavaScript noch nicht vertraut sind, verwenden Sie var i in aden Index und erwarten Sie, dass er eine Ganzzahl ist. Wenn Sie so etwas tun, a[i+offset] = <value>werden Werte an völlig falschen Stellen platziert. ("1" + 1 == "11").
Szmoore
8

Ein wichtiger Aspekt ist, dass for...innur Eigenschaften durchlaufen werden, die in einem Objekt enthalten sind und deren Attribut enumerable property auf true gesetzt ist. Wenn man also versucht, ein Objekt mit zu iterieren, for...inwerden möglicherweise beliebige Eigenschaften übersehen, wenn das Attribut enumerable property false ist. Es ist durchaus möglich, das Attribut enumerable property für normale Array-Objekte so zu ändern, dass bestimmte Elemente nicht aufgelistet werden. Im Allgemeinen gelten die Eigenschaftsattribute jedoch eher für Funktionseigenschaften innerhalb eines Objekts.

Sie können den Wert des aufzählbaren Eigenschaftsattributs einer Eigenschaft überprüfen, indem Sie:

myobject.propertyIsEnumerable('myproperty')

Oder um alle vier Eigenschaftsattribute zu erhalten:

Object.getOwnPropertyDescriptor(myobject,'myproperty')

Dies ist eine in ECMAScript 5 verfügbare Funktion. In früheren Versionen war es nicht möglich, den Wert des Attributs enumerable property zu ändern (es wurde immer auf true gesetzt).

Pierz
quelle
8

Das for/ inarbeitet mit zwei Arten von Variablen: Hashtabellen (assoziative Arrays) und Array (nicht assoziativ).

JavaScript bestimmt automatisch, wie es durch die Elemente geht. Wenn Sie also wissen, dass Ihr Array wirklich nicht assoziativ ist, können Sie es verwenden for (var i=0; i<=arrayLen; i++)und die Iteration für die automatische Erkennung überspringen.

Aber meiner Meinung nach ist es besser, for/ zu verwenden in. Der für diese automatische Erkennung erforderliche Prozess ist sehr klein.

Eine echte Antwort darauf hängt davon ab, wie der Browser den JavaScript-Code analysiert / interpretiert. Es kann zwischen Browsern wechseln.

Ich kann mir keine anderen Zwecke vorstellen, um for/ nicht zu verwenden in.

//Non-associative
var arr = ['a', 'b', 'c'];
for (var i in arr)
   alert(arr[i]);

//Associative
var arr = {
   item1 : 'a',
   item2 : 'b',
   item3 : 'c'
};

for (var i in arr)
   alert(arr[i]);
Ricardo
quelle
true, es sei denn, Sie verwenden prototypisierte Objekte. ;) unten
Ricardo
Das ist, weil Arrayes Objectauch ist
Kostenlose Beratung
2
for ... inarbeitet mit Objekten. Es gibt keine automatische Erkennung.
Ein besserer Oliver
7

Weil es über Eigenschaften iteriert, die zu Objekten in der Prototypenkette gehören, wenn Sie nicht vorsichtig sind.

Sie können verwenden for.. in, stellen Sie jedoch sicher, dass Sie jede Eigenschaft mit hasOwnProperty überprüfen .

JAL
quelle
2
Nicht genug - es ist vollkommen in Ordnung, Array-Instanzen beliebig benannte Eigenschaften hinzuzufügen, und diese werden anhand truevon hasOwnProperty()Überprüfungen getestet .
Pointy
Guter Punkt, danke. Ich war noch nie dumm genug, das selbst einem Array anzutun, also habe ich das nicht in Betracht gezogen!
JAL
1
@Pointy Ich habe dies nicht getestet, aber möglicherweise kann dies durch eine isNaNÜberprüfung jedes Eigenschaftsnamens behoben werden .
WynandB
1
@ Wynand interessante Idee; Ich verstehe jedoch nicht wirklich, warum es die Mühe wert ist, wenn das Iterieren mit einem einfachen numerischen Index so einfach ist.
Pointy
@WynandB Entschuldigung für die Beule, aber ich denke, eine Korrektur ist angebracht: Um isNaNzu überprüfen, ob eine Variable der spezielle Wert NaN ist oder nicht, kann sie nicht verwendet werden, um nach 'anderen Dingen als Zahlen' zu suchen (Sie können mit einem regulären Wert gehen Art dafür).
Doldt
6

Es ist nicht unbedingt schlecht (basierend auf dem, was Sie tun), aber im Fall von Arrays werden Array.prototypeSie seltsame Ergebnisse erzielen, wenn etwas hinzugefügt wurde. Wo Sie erwarten würden, dass diese Schleife dreimal ausgeführt wird:

var arr = ['a','b','c'];
for (var key in arr) { ... }

Wenn eine Funktion aufgerufen helpfulUtilityMethodhinzugefügt wurde Array‚s prototype, dann würde Ihre Schleife viermal läuft am Ende: keywäre 0, 1, 2, und helpfulUtilityMethod. Wenn Sie nur ganze Zahlen erwartet haben, hoppla.

josh3736
quelle
6

Sie sollten die for(var x in y)nur für Eigenschaftslisten verwenden, nicht für Objekte (wie oben erläutert).

user268396
quelle
13
Nur ein Hinweis zu SO - es gibt kein "oben", da Kommentare die Reihenfolge auf der Seite ständig ändern. Wir wissen also nicht genau, welchen Kommentar Sie meinen. Aus diesem Grund ist es gut, "im Kommentar einer Person" zu sagen.
JAL
@ JAL ... oder füge den Permalink zur Antwort hinzu.
WynandB
5

Die Verwendung der for...inSchleife für ein Array ist nicht falsch, obwohl ich mir vorstellen kann, warum Ihnen jemand das gesagt hat:

1.) Es gibt bereits eine Funktion oder Methode höherer Ordnung, die diesen Zweck für ein Array hat, aber mehr Funktionalität und schlankere Syntax hat, genannt 'forEach': Array.prototype.forEach(function(element, index, array) {} );

2.) Arrays haben immer eine Länge, aber for...inund forEachnicht eine Funktion für einen beliebigen Wert auszuführen, ist 'undefined'nur für die Indizes , die einen Wert definiert haben. Wenn Sie also nur einen Wert zuweisen, führen diese Schleifen eine Funktion nur einmal aus. Da ein Array jedoch aufgelistet ist, hat es immer eine Länge bis zum höchsten Index, der einen definierten Wert hat. Diese Länge kann jedoch bei Verwendung dieser Werte unbemerkt bleiben Schleifen.

3.) Der Standard für die Schleife führt eine Funktion so oft aus, wie Sie in den Parametern definiert haben. Da ein Array nummeriert ist, ist es sinnvoller zu definieren, wie oft Sie eine Funktion ausführen möchten. Im Gegensatz zu den anderen Schleifen kann die for-Schleife dann für jeden Index im Array eine Funktion ausführen, unabhängig davon, ob der Wert definiert ist oder nicht.

Im Wesentlichen können Sie jede Schleife verwenden, aber Sie sollten sich genau daran erinnern, wie sie funktionieren. Verstehen Sie die Bedingungen, unter denen sich die verschiedenen Schleifen wiederholen, ihre getrennten Funktionen und stellen Sie fest, dass sie für unterschiedliche Szenarien mehr oder weniger geeignet sind.

Es kann auch als eine bessere Vorgehensweise angesehen werden, die forEachMethode als die for...inSchleife im Allgemeinen zu verwenden, da sie einfacher zu schreiben ist und über mehr Funktionen verfügt. Daher möchten Sie möglicherweise die Gewohnheit haben, nur diese Methode und den Standard für, aber für Ihre zu verwenden Anruf.

Siehe unten, dass die ersten beiden Schleifen die Anweisungen console.log nur einmal ausführen, während der Standard für die Schleife die Funktion so oft ausführt, wie angegeben, in diesem Fall array.length = 6.

var arr = [];
arr[5] = 'F';

for (var index in arr) {
console.log(index);
console.log(arr[index]);
console.log(arr)
}
// 5
// 'F'
// => (6) [undefined x 5, 6]

arr.forEach(function(element, index, arr) {
console.log(index);
console.log(element);
console.log(arr);
});
// 5
// 'F'
// => Array (6) [undefined x 5, 6]

for (var index = 0; index < arr.length; index++) {
console.log(index);
console.log(arr[index]);
console.log(arr);
};
// 0
// undefined
// => Array (6) [undefined x 5, 6]

// 1
// undefined
// => Array (6) [undefined x 5, 6]

// 2
// undefined
// => Array (6) [undefined x 5, 6]

// 3
// undefined
// => Array (6) [undefined x 5, 6]

// 4
// undefined
// => Array (6) [undefined x 5, 6]

// 5
// 'F'
// => Array (6) [undefined x 5, 6]
mrmaclean89
quelle
4

Hier sind die Gründe, warum dies (normalerweise) eine schlechte Praxis ist:

  1. for...inSchleifen durchlaufen alle ihre eigenen aufzählbaren Eigenschaften und die aufzählbaren Eigenschaften ihrer Prototypen. Normalerweise möchten wir in einer Array-Iteration nur über das Array selbst iterieren. Und obwohl Sie selbst möglicherweise nichts zum Array hinzufügen, können Ihre Bibliotheken oder Ihr Framework etwas hinzufügen.

Beispiel :

Array.prototype.hithere = 'hithere';

var array = [1, 2, 3];
for (let el in array){
    // the hithere property will also be iterated over
    console.log(el);
}

  1. for...inSchleifen garantieren keine bestimmte Iterationsreihenfolge . Obwohl die Reihenfolge heutzutage in den meisten modernen Browsern üblich ist, gibt es immer noch keine 100% ige Garantie.
  2. for...inSchleifen ignorieren undefinedArray-Elemente, dh Array-Elemente, die noch nicht zugewiesen wurden.

Beispiel: :

const arr = []; 
arr[3] = 'foo';   // resize the array to 4
arr[4] = undefined; // add another element with value undefined to it

// iterate over the array, a for loop does show the undefined elements
for (let i = 0; i < arr.length; i++) {
    console.log(arr[i]);
}

console.log('\n');

// for in does ignore the undefined elements
for (let el in arr) {
    console.log(arr[el]);
}

Willem van der Veen
quelle
2

for ... in ist nützlich, wenn Sie an einem Objekt in JavaScript arbeiten, aber nicht für ein Array. Wir können jedoch nicht sagen, dass es ein falscher Weg ist, aber es wird nicht empfohlen. Sehen Sie sich dieses Beispiel unten an, indem Sie for ... in loop verwenden:

let txt = "";
const person = {fname:"Alireza", lname:"Dezfoolian", age:35}; 
for (const x in person) {
    txt += person[x] + " ";
}
console.log(txt); //Alireza Dezfoolian 35 

OK, machen wir es jetzt mit Array :

let txt = "";
const person = ["Alireza", "Dezfoolian", 35]; 
for (const x in person) {
   txt += person[x] + " ";
}
console.log(txt); //Alireza Dezfoolian 35 

Wie Sie das Ergebnis gleich sehen ...

Aber lasst uns etwas ausprobieren, einen Prototyp für Array erstellen ...

Array.prototype.someoneelse = "someoneelse";

Jetzt erstellen wir ein neues Array ();

let txt = "";
const arr = new Array();
arr[0] = 'Alireza';
arr[1] = 'Dezfoolian';
arr[2] = 35;
for(x in arr) {
 txt += arr[x] + " ";
}
console.log(txt); //Alireza Dezfoolian 35 someoneelse

Sie sehen die andere !!! ... In diesem Fall durchlaufen wir tatsächlich ein neues Array-Objekt!

Das ist einer der Gründe, warum wir es sorgfältig verwenden müssen, aber es ist nicht immer der Fall ...

Alireza
quelle
2

Eine for ... in-Schleife zählt immer die Schlüssel auf. Objekteigenschaftsschlüssel sind immer Zeichenfolgen, auch die indizierten Eigenschaften eines Arrays:

var myArray = ['a', 'b', 'c', 'd'];
var total = 0
for (elem in myArray) {
  total += elem
}
console.log(total); // 00123
Maher Tliba
quelle
0

Obwohl diese Frage nicht speziell angesprochen wird, möchte ich hinzufügen, dass es einen sehr guten Grund gibt, niemals für ... in a zu verwenden NodeList(wie man es von einem querySelectorAllAufruf erhalten würde, da es stattdessen die zurückgegebenen Elemente überhaupt nicht sieht Iteration nur über die NodeList-Eigenschaften.

Im Falle eines einzelnen Ergebnisses erhielt ich:

var nodes = document.querySelectorAll(selector);
nodes
 NodeList [a._19eb]
for (node in nodes) {console.log(node)};
VM505:1 0
VM505:1 length
VM505:1 item
VM505:1 entries
VM505:1 forEach
VM505:1 keys
VM505:1 values

was erklärte, warum meine for (node in nodes) node.href = newLink;versagte.

jcomeau_ictx
quelle