Die Reihenfolge der Elemente erfolgt in einer „for (… in…)“ - Schleife

204

Durchläuft die Schleife "for ... in" in Javascript die Hashtabellen / Elemente in der Reihenfolge, in der sie deklariert sind? Gibt es einen Browser, der dies nicht in der richtigen Reihenfolge tut?
Das Objekt Ich möchte Verwendung wird deklariert werden einmal und nie geändert werden.

Angenommen, ich habe:

var myObject = { A: "Hello", B: "World" };

Und ich benutze sie weiter in:

for (var item in myObject) alert(item + " : " + myObject[item]);

Kann ich erwarten, dass 'A: "Hallo" in den meisten anständigen Browsern immer vor' B: "Welt" steht?

Chakrit
quelle
61
Weil sie nur eine Teilmenge potenzieller Browser und Varianten testen würden. Ganz zu schweigen von zukünftigen Browsern. Es ist eindeutig falsch anzunehmen, dass ein nicht fehlgeschlagener Test einen konkreten Beweis liefert.
James Hughes
6
Ich bezweifle, dass meine eigene eingeschränkte Javascript-Fähigkeit besser sein wird als die der SO-Leute. Außerdem wer weiß, welcher seltsame Browser da draußen lauert? Und Sie können in der Antwort sehen, dass GChrome einen Fehler hat, der in meinem einfachen Beispielfall nicht erkennbar ist.
Chakrit

Antworten:

214

Zitiert John Resig :

Derzeit durchlaufen alle gängigen Browser die Eigenschaften eines Objekts in der Reihenfolge, in der sie definiert wurden. Chrome tut dies auch, mit Ausnahme einiger Fälle. [...] Dieses Verhalten wird in der ECMAScript-Spezifikation explizit nicht definiert. In ECMA-262, Abschnitt 12.6.4:

Die Mechanik der Aufzählung der Eigenschaften ... ist implementierungsabhängig.

Die Spezifikation unterscheidet sich jedoch erheblich von der Implementierung. Alle modernen Implementierungen von ECMAScript durchlaufen die Objekteigenschaften in der Reihenfolge, in der sie definiert wurden. Aus diesem Grund hat das Chrome-Team dies als Fehler eingestuft und wird es beheben.

Alle Browser respektieren die Definitionsreihenfolge mit Ausnahme von Chrome und Opera, die für jeden nicht numerischen Eigenschaftsnamen gelten. In diesen beiden Browsern werden die Eigenschaften in der Reihenfolge vor der ersten nicht numerischen Eigenschaft abgerufen (dies hängt damit zusammen, wie sie Arrays implementieren). Die Reihenfolge ist auch für die gleiche Object.keys.

Dieses Beispiel soll deutlich machen, was passiert:

var obj = {
  "first":"first",
  "2":"2",
  "34":"34",
  "1":"1",
  "second":"second"
};
for (var i in obj) { console.log(i); };
// Order listed:
// "1"
// "2"
// "34"
// "first"
// "second"

Die technischen Details sind weniger wichtig als die Tatsache, dass sich dies jederzeit ändern kann. Verlassen Sie sich nicht darauf, dass die Dinge so bleiben.

Kurz gesagt: Verwenden Sie ein Array, wenn Ihnen die Reihenfolge wichtig ist.

Borgar
quelle
3
Nicht wirklich. Chrome implementiert nicht die gleiche Reihenfolge wie andere Browser: code.google.com/p/v8/issues/detail?id=164
Tim Down
19
"Verwenden Sie ein Array, wenn die Reihenfolge für Sie wichtig ist": Was ist, wenn Sie JSON verwenden?
HM2K
11
@ HM2K, Gleiche Sache, Spezifikation sagt "Ein Objekt ist eine ungeordnete Sammlung von null oder mehr Name / Wert-Paaren." JSON ist kein JavaScript: Ein Server muss die Reihenfolge, in der Sie ihn übergeben, nicht einhalten (und wird dies wahrscheinlich auch nicht tun).
Borgar
7
Firefox scheint seit seiner 21-Version die Einfügereihenfolge nicht mehr zu respektieren.
Doc Davluz
2
Diese Antwort ist in ES2015 falsch.
Benjamin Gruenbaum
54

Ein Jahr später ...

Es ist 2012 und die wichtigsten Browser unterscheiden sich immer noch :

function lineate(obj){
    var arr = [], i;
    for (i in obj) arr.push([i,obj[i]].join(':'));
    console.log(arr);
}
var obj = { a:1, b:2, c:3, "123":'xyz' };
/* log1 */  lineate(obj);
obj.a = 4;
/* log2 */  lineate(obj);
delete obj.a;
obj.a = 4;
/* log3 */  lineate(obj);

Kern oder Test im aktuellen Browser

Safari 5, Firefox 14

["a:1", "b:2", "c:3", "123:xyz"]
["a:4", "b:2", "c:3", "123:xyz"]
["b:2", "c:3", "123:xyz", "a:4"]

Chrome 21, Opera 12, Knoten 0.6, Firefox 27

["123:xyz", "a:1", "b:2", "c:3"]
["123:xyz", "a:4", "b:2", "c:3"]
["123:xyz", "b:2", "c:3", "a:4"]

IE9

[123:xyz,a:1,b:2,c:3] 
[123:xyz,a:4,b:2,c:3] 
[123:xyz,a:4,b:2,c:3] 
dvdrtrgn
quelle
16
Was ist das Problem hier? Genau wie in der Spezifikation angegeben, handelt es sich um eine ungeordnete Liste von Schlüssel / Wert-Paaren.
Nickf
5
nickf: Die Implementierungen sind insofern richtig, als sie das tun, was die Spezifikation sagt. Ich denke, alle sind sich einig, dass die Javascript-Spezifikation ... nun, ich möchte das Wort "falsch" nicht verwenden, wie wäre es mit "extrem dumm und nervig"? : P
Boxed
10
@boxed: Stellen Sie sich Objekte als Hash-Map vor (Tabelle / was auch immer). In den meisten Sprachen (Java, Python usw.) werden diese Arten von Datenstrukturen nicht sortiert. Es ist daher nicht verwunderlich, dass dies auch in JavaScript der Fall ist, und es macht die Spezifikation sicherlich nicht falsch oder dumm.
Felix Kling
9
Ich verstehe den Sinn dieser Antwort ehrlich gesagt nicht (sorry). Die Reihenfolge, in der die Eigenschaften durchlaufen werden, ist ein Implementierungsdetail. Browser verwenden unterschiedliche JavaScript-Engines. Daher wird erwartet, dass die Reihenfolge unterschiedlich ist. Das wird sich nicht ändern.
Felix Kling
2
In einer Implementierung auf dem neuesten Stand der Technik erhalten Sie ganzzahlige Eigenschaften in numerischer Wertreihenfolge, da sie von einem realen Array unterstützt werden, und benannte Eigenschaften werden aufgrund versteckter Klassen- / Formulierungsformulierungen in Einfügereihenfolge empfangen. Was variieren kann, ist, ob sie zuerst die ganzzahligen Eigenschaften oder zuerst die benannten Eigenschaften auflisten. Das Hinzufügen von deleteist interessant, da zumindest in V8 das Objekt sofort durch eine Hash-Tabelle gesichert wird. Die Hash-Tabelle in V8 wird jedoch in der Einfügereihenfolge gespeichert. Das interessanteste Ergebnis hier ist IE, ich frage mich, was für eine Hässlichkeit sie tun, um das durchzuziehen ...
Esailija
27

Aus der ECMAScript-Sprachspezifikation , Abschnitt 12.6.4 (in der for .. inSchleife):

Die Mechanik der Aufzählung der Eigenschaften ist implementierungsabhängig. Die Reihenfolge der Aufzählung wird vom Objekt definiert.

Und Abschnitt 4.3.3 (Definition von "Objekt"):

Es ist eine ungeordnete Sammlung von Eigenschaften, von denen jede einen primitiven Wert, ein Objekt oder eine Funktion enthält. Eine in einer Eigenschaft eines Objekts gespeicherte Funktion wird als Methode bezeichnet.

Ich denke, das bedeutet, dass Sie sich nicht darauf verlassen können, dass die Eigenschaften in JavaScript-Implementierungen in einer einheitlichen Reihenfolge aufgelistet werden. (Es wäre sowieso ein schlechter Stil, sich auf implementierungsspezifische Details einer Sprache zu verlassen.)

Wenn Sie möchten, dass Ihre Reihenfolge definiert wird, müssen Sie etwas implementieren, das sie definiert, z. B. ein Array von Schlüsseln, die Sie sortieren, bevor Sie damit auf das Objekt zugreifen.

Tomalak
quelle
10

Die Elemente eines Objekts, die für / in Aufzählungen aufgeführt sind, sind die Eigenschaften, für die das DontEnum-Flag nicht gesetzt ist. Der ECMAScript-Standard, auch bekannt als Javascript, besagt ausdrücklich, dass "Ein Objekt eine ungeordnete Sammlung von Eigenschaften ist" (siehe http://www.mozilla.org/js/language/E262-3.pdf, Abschnitt 8.6).

Es wird nicht standardkonform (dh sicher) sein, anzunehmen, dass alle Javascript-Implementierungen in der Reihenfolge der Deklaration aufgelistet werden.

Adam Wright
quelle
2
Deshalb stellt er die Frage, anstatt nur anzunehmen: p
Jamie Pate
4

Die Iterationsreihenfolge wird auch in Bezug auf das Löschen von Eigenschaften verwechselt, in diesem Fall jedoch nur mit dem IE.

var obj = {};
obj.a = 'a';
obj.b = 'b';
obj.c = 'c';

// IE allows the value to be deleted...
delete obj.b;

// ...but remembers the old position if it is added back later
obj.b = 'bb';
for (var p in obj) {
    alert(obj[p]); // in IE, will be a, bb, then c;
                   // not a, c, then bb as for FF/Chrome/Opera/Safari
}

Der Wunsch, die Spezifikation zu ändern, um die Iterationsreihenfolge zu korrigieren, scheint unter Entwicklern ein recht beliebter Wunsch zu sein, wenn die Diskussion unter http://code.google.com/p/v8/issues/detail?id=164 ein Hinweis ist.

Brett Zamir
quelle
3

In IE6 ist die Reihenfolge nicht garantiert.


quelle
2

Der Bestellung kann nicht vertraut werden. Sowohl Opera als auch Chrome geben die Liste der ungeordneten Eigenschaften zurück.

<script type="text/javascript">
var username = {"14719":"A","648":"B","15185":"C"};

for (var i in username) {
  window.alert(i + ' => ' + username[i]);
}
</script>

Der obige Code zeigt B, A, C in Opera und C, A, B in Chrome.

Kouber Saparev
quelle
1

Dies beantwortet die Frage an sich nicht, bietet aber eine Lösung für das Grundproblem.

Angenommen, Sie können sich nicht auf die Reihenfolge verlassen, um sie zu erhalten, warum nicht ein Array von Objekten mit Schlüssel und Wert als Eigenschaften verwenden?

var myArray = [
    {
        'key'   : 'key1'
        'value' : 0
    },
    {
        'key'   : 'key2',
        'value' : 1
    } // ...
];

Jetzt müssen Sie sicherstellen, dass die Schlüssel eindeutig sind (vorausgesetzt, dies ist auch für Sie wichtig. Auch direkte Adressierungsänderungen und für (... in ...) geben jetzt Indizes als 'Schlüssel' zurück.

> console.log(myArray[0].key);
key1

> for (let index in myArray) {console.log(myArray[index].value);}
0
1
Siehe den Stift für (... in ...) Adressierung in der Reihenfolge von JDQ ( @JDQ ) auf CodePen .

JDQ
quelle