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

Mir wurde gesagt, ich solle keine Arrays in Javascript verwenden. Warum nicht?

1579
01 февр. LYriCAlsSH eingestellt 01 Feb. 2009-02-01 12:46 09 um 12:46 2009-02-01 12:46
@ 25 Antworten

Der Grund ist diese eine Konstruktion:

Die for-in Operation selbst ist keine schlechte Praxis, kann jedoch missbraucht werden, um beispielsweise Arrays oder Objekte eines Array-Typs zu durchlaufen.

Der for-in Operator dient dazu, die Eigenschaften eines Objekts aufzulisten. Dieser Operator wächst in einer Kette von Prototypen und listet auch vererbte Eigenschaften auf, was manchmal nicht erwünscht ist.

Darüber hinaus wird die Reihenfolge der Iterationen durch die Spezifikation nicht 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 numerischer Reihenfolge besucht werden.

In JScript (IE <= 8) wird beispielsweise die Reihenfolge der Aufzählung selbst in Array-Objekten 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 von vererbten Eigenschaften sprechen, wenn Sie beispielsweise das Array.prototype Objekt erweitern (beispielsweise einige Bibliotheken wie MooTools), werden auch diese Eigenschaften aufgelistet:

border=0
 Array.prototype.last = function () { return this[this.length-1]; }; for (var p in []) { // an empty array // last will be enumerated } 

Wie bereits erwähnt, sollten Sie zum Durchlaufen von Arrays oder Objekten wie einem Array am besten eine sequentielle Schleife verwenden, z. B. einen einfachen Start for / while .

Wenn Sie nur die eigenen Eigenschaften des Objekts hasOwnProperty möchten (diejenigen, die nicht geerbt werden), können Sie die Methode hasOwnProperty verwenden:

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

Und manche Leute empfehlen sogar, die Methode direkt aus Object.prototype , um Probleme zu vermeiden, wenn jemand unserem Objekt eine Eigenschaft namens hasOwnProperty hinzufügt:

 for (var prop in obj) { if (Object.prototype.hasOwnProperty.call(obj, prop)) { // prop is not inherited } } 
359
24 нояб. die antwort wird von CMS 24 nov gegeben. 2010-11-24 00:22 '10 am 0:22 2010-11-24 00:22

Es gibt drei Gründe, warum Sie for..in nicht verwenden for..in , um die Elemente eines Arrays zu for..in :

  • for..in deckt alle eigenen und geerbten Eigenschaften eines Array-Objekts ab, die nicht DontEnum . Dies bedeutet, dass wenn jemand Eigenschaften zu einem bestimmten Array-Objekt hinzufügt (es gibt gute Gründe dafür - ich habe es selbst getan) oder Array.prototype (was als schlechte Praxis in Code gilt, der mit anderen Skripten gut funktionieren sollte) diese Array.prototype ändert Eigenschaften werden auch wiederholt; Vererbte Eigenschaften können durch Überprüfen von hasOwnProperty() werden. Dies hilft Ihnen jedoch nicht bei den Eigenschaften, die im Array-Objekt selbst festgelegt sind

  • for..in nicht garantiert werden, dass die Reihenfolge der Elemente erhalten bleibt

  • es ist >

101
01 февр. Antwort von Christoph 01. Februar 2009-02-01 17:04 09 um 17:04 2009-02-01 17:04

Denn für ... in der Aufzählung durch das Objekt, das das Array enthält, und nicht das Array selbst. Wenn ich der Array-Prototypkette eine Funktion hinzufüge, wird diese ebenfalls eingeschlossen. Also.

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

Es wird schreiben:

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

Und da Sie nie sicher sein können, dass nichts zur Prototypkette 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]); } 

Es wird schreiben:

 0 = foo 1 = bar
49
01 февр. Die Antwort ist gegeben Pim Jager 01 Feb. 2009-02-01 13:08 09 um 13:08 2009-02-01 13:08

Die Verwendung von In-In-Arrays ist nicht reichlich. For-in durchläuft die Eigenschaftsnamen des Objekts, und im Falle eines Out-of-the-Box-Arrays entsprechen die Eigenschaften den Array-Indizes. (Eingebaute Attribute wie length , toString usw. Nicht in der Iteration enthalten.)

Wenn Ihr Code (oder die verwendete Struktur) jedoch Arrays oder einem Prototyp eines Arrays benutzerdefinierte Eigenschaften hinzufügt, werden diese Eigenschaften in die Iteration einbezogen, was wahrscheinlich nicht Ihren Vorstellungen entspricht.

Einige JS-Strukturen, wie z. B. Prototyp, modifizieren den Array-Prototyp. Andere Strukturen, wie z. B. jQuery, funktionieren nicht. Daher können Sie das In-In mit jQuery sicher verwenden.

Im Zweifelsfall sollten Sie das For-In wahrscheinlich nicht verwenden.

Eine Alternative 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 das Javascript-Array "Löcher" haben kann. Wenn Sie arr definieren als:

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

Dann hat das Array zwei Elemente, aber die Länge ist 101. Die Verwendung von for-in ergibt zwei Indizes, während for-loop den Index 101 ergibt, wobei 99 nicht undefined .

37
01 февр. Die Antwort gibt JacquesB vom 1. Februar. 2009-02-01 13:34 09 um 13:34 2009-02-01 13:34 Uhr

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ählervariable rechnen müssen, da die Schleife über die Objekteigenschaftsnamen durchläuft und die Variable daher eine Zeichenfolge ist.

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

dann

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

werde schreiben

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

Natürlich ist es durch Drehen leicht zu überwinden

 ii = parseInt(ii); 

in einer Schleife, aber die erste Struktur ist direkter.

29
02 сент. Die Antwort ist ctmiddle 02 Sep. gegeben. 2009-09-02 05:29 09 um 02:29 2009-09-02 05:29

Ab 2016 (ES6) können wir for…of ein Array durchlaufen, wie John Slegers bereits festgestellt hat.

Ich wollte nur diesen einfachen Demo-Code hinzufügen, um alles 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 von 0 bis 5 berechnet und ignoriert auch Array.prototype.foo . Es zeigt die Array-Werte. .

  • for...in Listen nur 5 , ignoriert die Array-Indizes undefined, fügt jedoch foo hinzu. Es zeigt die Namen der Eigenschaften des Arrays. .

26
10 марта '16 в 7:29 2016-03-10 07:29 Die Antwort ist gegeben MarcG 10. März 16 um 07:29 2016-03-10 07:29

Neben der Tatsache, dass for ... in alle aufzählbaren Eigenschaften auflistet (dies ist nicht dasselbe wie "alle Elemente eines Arrays"!), Siehe http://www.ecma-international.org/publications/files/ECMA-ST /Ecma-262.pdf , Abschnitt 12.6.4 (fünfte Ausgabe) oder 13.7.5.15 (7. Ausgabe):

Die Mechanik und Reihenfolge der Auflistungseigenschaften ... nicht angegeben ...

(Meine unterstreichen.)

Das bedeutet, dass der Browser die Eigenschaften in der Reihenfolge durchlaufen kann, in der er vom Browser gewünscht wurde. Oder in numerischer Reihenfolge. Oder in der lexikalischen Reihenfolge (wo "30" auf "4" steht!) Denken Sie daran, dass alle Objektschlüssel - und daher alle Array-Indizes - tatsächlich Strings sind, also macht es Sinn. Es kann einen Bucket passieren, wenn es Objekte als Hashtabellen implementiert. Oder nimm irgendetwas davon und füge "zurück" hinzu. Der Browser kann sogar zufällig iterieren und mit ECMA-262 kompatibel sein, wenn er jede Eigenschaft genau einmal besucht hat.

In der Praxis lieben es die meisten Browser, in der gleichen Reihenfolge zu iterieren. Aber nichts sagt, was Sie brauchen. Diese Implementierung ist spezifisch und kann sich jederzeit ändern, wenn eine andere Methode effizienter ist.

Jedenfalls trägt for ... in keine Konnotation der Ordnung. Wenn Sie Interesse an der Bestellung haben, seien Sie offen und verwenden Sie eine reguläre for Schleife mit einem Index.

21
14 мая '12 в 19:26 2012-05-14 19:26 Die Antwort wird von chao am 14. Mai 12 um 19:26 2012-05-14 19:26 gegeben

Die kurze Antwort lautet: Es lohnt sich einfach nicht.


Längere Antwort: Es lohnt sich einfach nicht, auch wenn die Reihenfolge der aufeinanderfolgenden Elemente und die optimale Leistung nicht erforderlich sind.


>

  • Die Verwendung von for (var i in array) {} führt dazu, dass das 'array' wie jedes andere reine Objekt interpretiert wird, die Eigenschaftskette des Objekts übergeben wird und letztendlich >for Schleife.
  • Es kann nicht garantiert werden, dass die Eigenschaften eines Objekts in einer sequentiellen Reihenfolge zurückgegeben werden.
  • Die Verwendung von hasOwnProperty() oder isNaN() Prüfungen zum Filtern von Objekteigenschaften ist ein zusätzlicher Aufwand, der dazu führt, dass das Objekt (noch >

Aus diesen Gründen gibt es nicht einmal einen akzeptablen Kompromiss zwischen Leistung und Komfort. In der Tat gibt es keinen Vorteil, wenn das Array als reines Objekt behandelt und Operationen an den Eigenschaften des Array-Objekts ausgeführt werden sollen.

20
14 марта '13 в 10:12 2013-03-14 10:12 Die Antwort gibt WynandB am 14. März 13 um 10:12 Uhr 2013-03-14 10:12

Grundsätzlich zwei Gründe:

Eins

Wie andere können Sie Schlüssel abrufen, die sich nicht in Ihrem Array befinden oder vom Prototyp übernommen wurden. Wenn die Bibliothek zum Beispiel eine Eigenschaft zu den Array- oder Object-Prototypen 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 Methode hasOwnProperty 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 von Objekten mit einer for-in-Schleife.

Zwei

Normalerweise ist die Reihenfolge der Elemente im Array wichtig, aber die for-in-Schleife wird nicht notwendigerweise in der richtigen Reihenfolge durchlaufen, da das Array als ein Objekt behandelt wird, das in JS implementiert ist, und nicht als Array. Das sieht nach einer kleinen Sache aus, kann aber Anwendungen wirklich durcheinander bringen und ist schwer zu debuggen.

15
04 февр. Antworten Lior 04 Feb. 2014-02-04 19:54 '14 um 19:54 2014-02-04 19:54

Weil es die Felder des Objekts auflistet, nicht die Indizes. Sie können den Wert mit dem Index "Länge" erhalten, und ich bezweifle, dass Sie ihn wollen.

15
01 февр. Die Antwort ist gegeben vava 01 Feb. 2009-02-01 12:50 09 um 12:50 2009-02-01 12:50

Das Problem ist mit for ... in ... - und es wird zu einem Problem, wenn der Programmierer die Sprache wirklich nicht versteht. es ist kein Fehler oder etwas anderes; ist, dass es über alle Elemente des Objekts iteriert (na ja, alle aufgelisteten Member, aber dieser Teil ist derzeit). Wenn Sie nur die indizierten Eigenschaften eines Arrays iterieren möchten, ist die einzige garantierte Methode zum Speichern semantisch konsistenter Werte die Verwendung eines ganzzahligen Index (d. H. Der for (var i = 0; i < array.length; ++i) style- for (var i = 0; i < array.length; ++i) ).

Jedem Objekt können beliebige Eigenschaften zugeordnet sein. Insbesondere beim Laden zusätzlicher Eigenschaften in eine Array-Instanz wäre nichts Schlimmes. Code, der nur indizierte Eigenschaften wie ein Array anzeigen möchte, muss also einen ganzzahligen Index einhalten. Ein Code, der das vollständig weiß for ... in und sollte eigentlich alle Eigenschaften sehen, na gut, dann ist das auch normal.

14
24 нояб. Die Antwort wird am 24. November gegeben. 2010-11-24 00:23 '10 am 0:23 2010-11-24 00:23

Ich glaube nicht, dass ich etwas hinzufügen muss. Die Antwort auf das Triptychon oder die CMS-Antwort, warum Sie in einigen Fällen die Verwendung von for-in vermeiden sollten.

Ich möchte jedoch hinzufügen, dass es in modernen Browsern ein alternatives In for-in , das in Fällen verwendet werden kann, in denen das In for-in nicht verwendet werden kann. Diese for-of Alternative ist:

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

Hinweis:

Leider unterstützt keine Version von Internet Explorer diese Funktion ( Edge 12+ ). Sie müssen daher etwas länger warten, bis Sie sie in Ihrem Produktionscode auf der Clientseite verwenden können. Die Verwendung in serverseitigem JS-Code (wenn Sie Node.js verwenden ) sollte jedoch sicher sein.

10
21 февр. Antwort von John Slegers 21 Feb. 2016-02-21 23:48 '16 um 23:48 Uhr 2016-02-21 23:48

Aufgrund der Semantik sind die Arrays for, in Pfaden (d. H. 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" 
8
25 окт. die antwort wird matpop 25 oct gegeben. 2013-10-25 19:43 '13 am 19:43 2013-10-25 19:43

TL DR: Die Verwendung einer for in Schleife in Arrays ist nicht böse, das Gegenteil ist der Fall.

Ich denke, die for in Schleife ist die Perle von JS, wenn sie ordnungsgemäß in Arrays verwendet wird. Sie müssen die vollständige Kontrolle über Ihre Software haben und wissen, was Sie tun. Sehen Sie sich die genannten Mängel an und widerlegen Sie sie nacheinander.

  • Es durchläuft auch geerbte Eigenschaften: Zunächst müssen alle Erweiterungen für Array.prototype mit Object.defineProperty() , und ihr enumerable Deskriptor muss auf false . Jede Bibliothek, die dies nicht tut, sollte überhaupt nicht verwendet werden.
  • Die Eigenschaften, die Sie der Vererbungskette hinzufügen, werden dann gezählt: Wenn Sie eine Array-Unterklasse für Object.setPrototypeOf oder für die extend Object.setPrototypeOf . Sie müssen Object.defineProperty() erneut verwenden, wodurch die writable , enumerable und configurable Eigenschaftsdeskriptoren standardmäßig auf false . Sehen wir uns hier ein Beispiel für eine Array-Subklasse an ...

7
28 янв. Antwort auf Redu Jan 28 2017-01-28 11:16 '17 um 11:16 2017-01-28 11:16

Ein wichtiger Aspekt ist, dass for...in nur die Eigenschaften des Objekts durchläuft , für die das Attribut "enumerable" auf "true" gesetzt ist. Wenn Sie also versuchen, ein Objekt mit for...in zu iterieren, können alle Eigenschaften übersprungen werden, wenn das Enumerable-Eigenschaftsattribut false ist. Es ist möglich, das aufzählbare Eigenschaftsattribut für gewöhnliche Array-Objekte so zu ändern, dass bestimmte Elemente nicht aufgelistet werden. Obwohl sich Eigenschaftsattribute in der Regel auf die Eigenschaften einer Funktion innerhalb eines Objekts beziehen.

Sie können den Attributwert der Enumerable-Eigenschaft der Eigenschaft überprüfen:

 myobject.propertyIsEnumerable('myproperty') 

Oder holen Sie sich alle vier Eigenschaftsattribute:

 Object.getOwnPropertyDescriptor(myobject,'myproperty') 

Diese Funktion ist in ECMAScript 5 verfügbar. In früheren Versionen war es nicht möglich, den Wert des Enumerable-Eigenschaftsattributs zu ändern (es war immer true).

7
10 мая '13 в 21:20 2013-05-10 21:20 Die Antwort wurde von Pierz am 10. Mai 13 um 21:20 Uhr 2013-05-10 21:20 gegeben

Neben anderen Problemen ist die Syntax "for..in" wahrscheinlich >

 var a = ["a"] for (var i in a) alert(typeof i) // 'string' for (var i = 0; i < a.length; i++) alert(typeof i) // 'number' 
7
06 июня '12 в 1:52 2012-06-06 01:52 Die Antwort ist gegeben am 1. Juni 12 um 01:52 Uhr 2012-06-06 01:52

for / in arbeitet mit zwei Arten von Variablen: Hashtabellen (assoziative Arrays) und ein Array (nicht assoziativ).

JavaScript legt automatisch fest, wie die Elemente durchlaufen werden. Wenn Sie wissen, dass Ihr Array wirklich nicht assoziativ ist, können Sie for (var i=0; i<=arrayLen; i++) und die Iteration der automatischen Erkennung überspringen.

Meiner Meinung nach ist es jedoch besser for / in , der für diese automatische Erkennung erforderliche Prozess ist sehr klein.

Die tatsächliche Antwort darauf hängt davon ab, wie der Browser den JavaScript-Code analysiert / interpretiert. Es kann zwischen Browsern variieren.

Ich kann nicht an andere Ziele denken: nicht for / 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]); 
7
24 нояб. Die Antwort wird von Ricardo am 24. November gegeben. 2010-11-24 00:44 '10 um 0:44 2010-11-24 00:44

Wenn Sie nicht aufpassen, werden die Eigenschaften, die zu den Objekten in der Prototypkette gehören, durchlaufen.

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

6
24 нояб. Die Antwort wird JAL 24 Nov. gegeben. 2010-11-24 00:20 '10 am 0:20 2010-11-24 00:20

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

5
24 нояб. Antwort von user268396 vom 24. November 2010-11-24 00:25 '10 am 0:25 2010-11-24 00:25

Dies ist nicht unbedingt schlecht (basierend auf dem, was Sie tun), aber im Falle von Arrays, wenn etwas zu Array.prototype hinzugefügt wird, erhalten Sie seltsame Ergebnisse. Wo erwarten Sie, dass dieser Zyklus dreimal abläuft:

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

Wenn dem Array prototype eine Funktion mit dem Namen helpfulUtilityMethod hinzugefügt wird, wird Ihr Zyklus viermal ausgeführt: Der key helpfulUtilityMethod 0 , 1 , 2 und helpfulUtilityMethod . Wenn Sie nur ganze Zahlen erwartet haben, oops.

5
24 нояб. die antwort ist gegeben josh3736 24 nov. 2010-11-24 00:25 '10 am 0:25 2010-11-24 00:25

Die Verwendung einer for...in Schleife for...in Array ist nicht falsch, obwohl ich vermuten kann, warum jemand Ihnen das gesagt hat:

1.) Es gibt bereits eine Funktion oder Methode höherer Ordnung, die dieses Ziel für ein Array hat, jedoch mehr Funktionalität und eine kompaktere Syntax mit der Bezeichnung "forEach" hat: Array.prototype.forEach(function(element, index, array) {} );

2.) Arrays haben immer length, aber for...in und forEach funktionieren keine Werte für 'undefined' , nur für Indizes, die einen bestimmten Wert haben. Wenn Sie nur einen Wert zuweisen, werden diese Zyklen nur einmal ausgeführt. Da das Array jedoch aufgeführt ist, hat es immer eine Länge bis zum höchsten Index, der einen bestimmten Wert hat. Diese Länge kann jedoch bei Verwendung dieser Schleifen unbemerkt bleiben.