Kolejność elementów w pętli „za (… w…)”

205

Czy pętla „for… in” w Javascript przechodzi przez tabele / elementy hasht w kolejności, w jakiej zostały zadeklarowane? Czy istnieje przeglądarka, która nie robi tego w kolejności?
Obiekt, którego chcę użyć, zostanie zadeklarowany raz i nigdy nie będzie modyfikowany.

Załóżmy, że mam:

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

I używam ich dalej w:

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

Czy mogę się spodziewać, że „A:„ Cześć ”” zawsze będzie występować przed „B:„ Świat ”” w większości porządnych przeglądarek?

czakryt
źródło
61
Ponieważ będą testować tylko podzbiór potencjalnych przeglądarek i wariantów. Nie wspominając o żadnych przyszłych przeglądarkach. Oczywistym błędem jest założenie, że bezbłędny test zapewnia jakikolwiek konkretny dowód.
James Hughes,
6
Wątpię, czy moja własna ograniczona zdolność javascript będzie lepsza niż tłum SO. Poza tym, kto wie, co czai się tam dziwna przeglądarka? I widać w odpowiedzi, że GChrome ma błąd, który nie będzie widoczny w moim prostym przykładzie.
chakrit

Odpowiedzi:

214

Cytując Johna Resiga :

Obecnie wszystkie główne przeglądarki przeglądają właściwości obiektu w kolejności, w jakiej zostały zdefiniowane. Chrome też to robi, z wyjątkiem kilku przypadków. [...] To zachowanie jest wyraźnie niezdefiniowane w specyfikacji ECMAScript. W ECMA-262 sekcja 12.6.4:

Mechanika wyliczania właściwości ... zależy od implementacji.

Jednak specyfikacja różni się od implementacji. Wszystkie współczesne implementacje ECMAScript iterują właściwości obiektu w kolejności, w jakiej zostały zdefiniowane. Z tego powodu zespół Chrome uznał to za błąd i naprawi to.

Wszystkie przeglądarki przestrzegają kolejności definicji, z wyjątkiem Chrome i Opery, które działają dla każdej nieliczbowej nazwy właściwości. W tych dwóch przeglądarkach właściwości są wyciągane w kolejności przed pierwszą właściwością nienumeryczną (ma to związek z tym, jak implementują tablice). Kolejność jest również taka sama Object.keys.

Ten przykład powinien wyjaśnić, co się dzieje:

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"

Techniczne tego są mniej ważne niż fakt, że może się to zmienić w dowolnym momencie. Nie polegaj na rzeczach pozostających w ten sposób.

W skrócie: użyj tablicy, jeśli kolejność jest dla Ciebie ważna.

Borgar
źródło
3
Nie całkiem. Chrome nie realizuje tej samej kolejności, co inne przeglądarki: code.google.com/p/v8/issues/detail?id=164
Tim Down
19
„Użyj tablicy, jeśli kolejność jest dla Ciebie ważna”: A co z korzystaniem z JSON?
HM2K,
11
@ HM2K, To samo, spec mówi „Obiekt to nieuporządkowany zbiór zer lub więcej par nazwa / wartość”. JSON nie jest JavaScript: Serwer nie musi (i prawdopodobnie nie będzie) szanować kolejności, w której go przekazujesz.
Borgar,
7
Firefox, ponieważ jego wersja 21 wydaje się nie przestrzegać kolejności wstawiania.
Doc Davluz,
2
Ta odpowiedź jest nieprawidłowa w ES2015.
Benjamin Gruenbaum
54

Wpadłem na to rok później ...

Jest rok 2012, a główne przeglądarki wciąż się różnią:

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);

Gist lub test w bieżącej przeglądarce

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, Node 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
źródło
16
w czym jest problem? Dokładnie tak, jak mówi specyfikacja, jest to nieuporządkowana lista par klucz / wartość.
nickf
5
nickf: implementacje mają rację, ponieważ robią to, co mówi specyfikacja. Myślę, że wszyscy zgadzają się, że specyfikacja javascript jest… cóż, nie chcę używać słowa „zły”, co powiesz na „wyjątkowo głupi i irytujący”? : P
zapakowano w pudełko
10
@ boxed: Pomyśl o obiektach jak o mapie mieszającej (tabela / cokolwiek). W większości języków (Java, Python itp.) Tego rodzaju struktury danych nie są sortowane. Nic więc dziwnego, że jest to ten sam przypadek również w JavaScript i na pewno nie powoduje, że specyfikacja jest zła lub głupia.
Felix Kling
9
Naprawdę nie rozumiem sensu tej odpowiedzi (przepraszam). Kolejność iteracji właściwości jest szczegółem implementacji, a przeglądarki używają różnych silników JavaScript, więc oczekuje się, że kolejność jest inna. To się nie zmieni.
Felix Kling
2
W najnowocześniejszej implementacji otrzymasz właściwości liczb całkowitych w kolejności wartości liczbowych dzięki wsparciu rzeczywistej tablicy, a nazwane właściwości otrzymamy w kolejności wstawiania ze względu na ukryte formułowanie klas / kształtów. To, co może się różnić, to to, czy najpierw wypisują właściwości liczb całkowitych, czy właściwości nazwane. Dodanie deletejest interesujące, ponieważ przynajmniej w wersji V8 natychmiast powoduje utworzenie kopii zapasowej obiektu przez tablicę skrótów. Jednak tabela skrótów w V8 przechowuje w kolejności wstawiania. Najciekawszym rezultatem jest IE, zastanawiam się, jaką brzydotę robią, aby to wyciągnąć ...
Esailija
27

Ze specyfikacji języka ECMAScript , sekcja 12.6.4 (w for .. inpętli):

Mechanika wyliczania właściwości zależy od implementacji. Kolejność wyliczania jest definiowana przez obiekt.

I sekcja 4.3.3 (definicja „obiektu”):

Jest to nieuporządkowany zbiór właściwości, z których każda zawiera pierwotną wartość, obiekt lub funkcję. Funkcja przechowywana we właściwości obiektu nazywa się metodą.

Myślę, że to oznacza, że ​​nie można polegać na właściwościach wyliczanych w spójnej kolejności we wszystkich implementacjach JavaScript. (W każdym razie oparcie się na szczegółach języka dotyczących konkretnej implementacji byłoby kiepskie).

Jeśli chcesz zdefiniować swoje zamówienie, musisz zaimplementować coś, co je definiuje, na przykład szereg kluczy, które sortujesz przed uzyskaniem dostępu do obiektu.

Tomalak
źródło
10

Elementy obiektu, które dla / w wyliczają są właściwościami, które nie mają ustawionej flagi DontEnum. Standard ECMAScript, czyli Javascript, wyraźnie stwierdza, że ​​„Obiekt jest nieuporządkowanym zbiorem właściwości” (patrz http://www.mozilla.org/js/language/E262-3.pdf sekcja 8.6).

Zakładanie, że wszystkie implementacje JavaScript będą wyliczane w kolejności deklaracji, nie będzie zgodne ze standardami (tj. Bezpieczne).

Adam Wright
źródło
2
dlatego zadaje pytanie, zamiast po prostu założyć: p
Jamie Pate
4

Kolejność iteracji jest również mylona w odniesieniu do usuwania właściwości, ale w tym przypadku tylko z 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
}

Pragnienie zmiany specyfikacji w celu ustalenia kolejności iteracji wydaje się dość popularne wśród programistów, jeśli dyskusja na stronie http://code.google.com/p/v8/issues/detail?id=164 jest jakąkolwiek wskazówką.

Brett Zamir
źródło
3

w IE6 kolejność nie jest gwarantowana.


źródło
2

Zamówienie nie może być zaufane. Zarówno Opera, jak i Chrome zwracają listę właściwości nieuporządkowanych.

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

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

Powyższy kod pokazuje B, A, C w Operze i C, A, B w Chrome.

Kouber Saparev
źródło
1

Nie odpowiada to samo w sobie na pytanie, ale oferuje rozwiązanie podstawowego problemu.

Zakładając, że nie możesz polegać na kolejności zachowania, dlaczego nie użyć tablicy obiektów z kluczem i wartością jako właściwości?

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

Teraz do Ciebie należy upewnienie się, że klucze są unikalne (przy założeniu, że jest to również ważne dla Ciebie. Również bezpośrednie adresowanie zmian i dla (... w ...) teraz zwraca indeksy jako „klucze”.

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

> for (let index in myArray) {console.log(myArray[index].value);}
0
1
Zobacz pióro do adresowania (... w ...) w kolejności według JDQ ( @JDQ ) na CodePen .

JDQ
źródło