Elemen memesan dalam loop "for (... in ...)"

205

Apakah "for ... in" loop dalam Javascript loop melalui hashtables / elemen dalam urutan mereka dinyatakan? Apakah ada peramban yang tidak melakukannya secara berurutan?
Objek yang ingin saya gunakan akan dideklarasikan sekali dan tidak akan pernah dimodifikasi.

Misalkan saya punya:

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

Dan saya selanjutnya menggunakannya dalam:

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

Dapatkah saya berharap 'A: "Hello"' selalu datang sebelum 'B: "World"' di sebagian besar browser yang layak?

chakrit
sumber
61
Karena mereka hanya akan menguji sebagian browser dan varian potensial. Belum lagi peramban di masa depan. Jelas salah untuk menganggap tes yang tidak gagal memberikan bukti konkret apa pun.
James Hughes
6
Saya ragu kemampuan javascript saya sendiri yang terbatas akan lebih baik daripada kerumunan SO. Selain itu siapa yang tahu apa peramban aneh bersembunyi di luar sana? Dan Anda dapat melihat di jawaban bahwa GChrome memang memiliki bug yang tidak akan terlihat dalam contoh kasus sederhana saya.
chakrit

Jawaban:

214

Mengutip John Resig :

Saat ini semua peramban utama mengulangi properti suatu objek sesuai urutan penetapannya. Chrome juga melakukan ini, kecuali untuk beberapa kasus. [...] Perilaku ini secara eksplisit dibiarkan tidak ditentukan oleh spesifikasi skrip ECMAS. Dalam ECMA-262, bagian 12.6.4:

Mekanisme penghitungan properti ... bergantung pada implementasi.

Namun, spesifikasi sangat berbeda dari implementasi. Semua implementasi modern dari ECMAScript iterate melalui properti objek dalam urutan di mana mereka didefinisikan. Karena itu, tim Chrome menganggap ini sebagai bug dan akan memperbaikinya.

Semua browser mematuhi urutan definisi dengan pengecualian Chrome dan Opera yang berlaku untuk setiap nama properti non-numerik. Dalam dua browser ini properti ditarik secara berurutan di depan properti non-numerik pertama (ini ada hubungannya dengan bagaimana mereka mengimplementasikan array). Urutannya sama Object.keysjuga.

Contoh ini harus menjelaskan apa yang terjadi:

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"

Teknisnya kurang penting daripada fakta bahwa ini dapat berubah setiap saat. Jangan mengandalkan hal-hal seperti ini.

Singkatnya: Gunakan array jika pesanan penting bagi Anda.

Borgar
sumber
3
Tidak juga. Chrome tidak menerapkan urutan yang sama dengan browser lain: code.google.com/p/v8/issues/detail?id=164
Tim Down
20
"Gunakan array jika pesanan penting bagi Anda": Bagaimana dengan saat Anda menggunakan JSON?
HM2K
11
@ HM2K, Hal yang sama, spec mengatakan, "Objek adalah kumpulan unordered dari nol atau lebih pasangan nama / nilai." JSON bukan JavaScript: Server tidak perlu (dan mungkin tidak) menghormati perintah yang Anda berikan.
Borgar
7
Firefox sejak versi 21 tampaknya tidak menghormati urutan penyisipan lagi.
Doc Davluz
3
Jawaban ini salah dalam ES2015.
Benjamin Gruenbaum
54

Menabrak ini setahun kemudian ...

Ini adalah tahun 2012 dan browser utama masih berbeda:

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

Intisari atau uji di browser saat ini

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
sumber
16
apa masalahnya di sini? Persis seperti spesifikasi mengatakan, ini adalah daftar pasangan kunci / val yang tidak terurut.
nickf
5
nickf: implementasinya benar karena mereka melakukan apa yang dikatakan oleh spec. Saya pikir semua orang setuju bahwa spesifikasi javascript adalah .. yah, saya tidak ingin menggunakan kata "salah", bagaimana dengan "sangat bodoh dan menyebalkan"? : P
kemas
10
@boxed: Pikirkan tentang objek sebagai peta hash (tabel / apa pun). Dalam sebagian besar bahasa (Java, Python, dll) jenis struktur data ini tidak diurutkan. Jadi tidak mengherankan bahwa ini adalah kasus yang sama dalam JavaScript juga dan tentu saja tidak membuat spesifikasi salah atau bodoh.
Felix Kling
9
Sejujurnya saya tidak mengerti maksud dari jawaban ini (maaf). Urutan di mana properti diulangi adalah detail implementasi, dan browser menggunakan mesin JavaScript yang berbeda, sehingga urutannya berbeda. Ini tidak akan berubah.
Felix Kling
2
Dalam implementasi seni Anda akan menerima properti integer dalam urutan nilai numerik karena didukung oleh array nyata, dan properti bernama akan diterima dalam urutan penyisipan karena formulasi kelas / bentuk tersembunyi. Yang dapat bervariasi adalah apakah mereka mencantumkan properti integer terlebih dahulu atau menamai properti terlebih dahulu. Penambahan deletemenarik karena setidaknya dalam V8, itu langsung menyebabkan objek didukung oleh tabel hash. Namun, tabel hash di V8 menyimpan dalam urutan penyisipan. Hasil yang paling menarik di sini adalah IE, saya bertanya-tanya bagaimana kejelekan yang mereka lakukan untuk melakukan itu ...
Esailija
28

Dari Spesifikasi Bahasa Naskah ECMAS, bagian 12.6.4 (di for .. inloop):

Mekanisme penghitungan properti bergantung pada implementasi. Urutan enumerasi ditentukan oleh objek.

Dan bagian 4.3.3 (definisi "Objek"):

Ini adalah kumpulan properti tanpa urutan yang masing-masing berisi nilai, objek, atau fungsi primitif. Fungsi yang disimpan dalam properti suatu objek disebut metode.

Saya kira itu berarti Anda tidak dapat mengandalkan properti yang disebutkan dalam urutan yang konsisten di seluruh implementasi JavaScript. (Lagi pula, itu akan menjadi gaya yang buruk untuk bergantung pada perincian khusus implementasi suatu bahasa.)

Jika Anda ingin pesanan Anda ditentukan, Anda perlu mengimplementasikan sesuatu yang mendefinisikannya, seperti array kunci yang Anda urutkan sebelum mengakses objek dengannya.

Tomalak
sumber
10

Elemen dari objek yang untuk / dalam penghitungan adalah properti yang tidak memiliki set flag DontEnum. ECMAScript, alias Javascript, standar secara eksplisit mengatakan bahwa "Suatu Objek adalah kumpulan properti yang tidak terurut" (lihat http://www.mozilla.org/js/language/E262-3.pdf bagian 8.6).

Ini tidak akan menjadi standar yang sesuai (yaitu aman) untuk mengasumsikan semua implementasi Javascript akan disebutkan dalam urutan deklarasi.

Adam Wright
sumber
2
itulah sebabnya dia mengajukan pertanyaan, alih-alih hanya mengasumsikan: p
Jamie Pate
5

Urutan perulangan juga bingung sehubungan dengan penghapusan properti, tetapi dalam hal ini hanya dengan 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
}

Keinginan untuk mengubah spesifikasi untuk memperbaiki urutan iterasi tampaknya menjadi keinginan yang cukup populer di kalangan pengembang jika diskusi di http://code.google.com/p/v8/issues/detail?id=164 adalah indikasi.

Brett Zamir
sumber
3

di IE6, pesanannya tidak dijamin.


sumber
2

Pesanan tidak dapat dipercaya. Baik Opera dan Chrome mengembalikan daftar properti yang tidak diurusi.

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

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

Kode di atas menunjukkan B, A, C di Opera dan C, A, B di Chrome.

Kouber Saparev
sumber
1

Ini tidak menjawab pertanyaan itu sendiri, tetapi menawarkan solusi untuk masalah dasar.

Dengan asumsi bahwa Anda tidak dapat bergantung pada pesanan untuk disimpan, mengapa tidak menggunakan array objek dengan kunci dan nilai sebagai properti?

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

Sekarang, terserah Anda untuk memastikan bahwa kunci-kunci itu unik (dengan anggapan bahwa ini juga penting bagi Anda. Selain itu, mengatasi perubahan secara langsung, dan untuk (... dalam ...) sekarang mengembalikan indeks sebagai 'kunci'.

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

> for (let index in myArray) {console.log(myArray[index].value);}
0
1
Lihat Pena untuk (... dalam ...) yang diatasi secara berurutan oleh JDQ ( @JDQ ) pada CodePen .

JDQ
sumber