Apakah JavaScript Menjamin Pesanan Properti Obyek?

647

Jika saya membuat objek seperti ini:

var obj = {};
obj.prop1 = "Foo";
obj.prop2 = "Bar";

Apakah objek yang dihasilkan akan selalu terlihat seperti ini?

{ prop1 : "Foo", prop2 : "Bar" }

Yaitu, akankah properti berada dalam urutan yang sama dengan yang saya tambahkan?

mellowsoon
sumber
1
@TJCrowder Bisakah Anda menjelaskan sedikit lebih rinci mengapa jawaban yang diterima tidak lagi akurat? Pertanyaan yang Anda tautkan tampaknya bermuara pada gagasan bahwa pesanan properti masih belum dijamin per spek.
zero298
2
@ zero298: Jawaban yang diterima untuk pertanyaan itu dengan jelas menjelaskan urutan properti yang ditentukan pada ES2015 +. Operasi Legacy ( for-in, Object.keys) tidak perlu mendukungnya (resmi), tapi ada yang pesan sekarang. (Secara tidak resmi: Firefox, Chrome, dan Edge semua mengikuti urutan yang ditentukan bahkan untuk masuk dan Object.keys, di mana mereka tidak secara resmi diharuskan untuk: jsfiddle.net/arhbn3k2/1 )
TJ Crowder

Jawaban:

474

Urutan iterasi untuk objek mengikuti serangkaian aturan tertentu sejak ES2015, tetapi tidak (selalu) mengikuti urutan penyisipan . Sederhananya, urutan iterasi adalah kombinasi dari urutan penyisipan untuk kunci string, dan urutan naik untuk kunci seperti angka:

// key order: 1, foo, bar
const obj = { "foo": "foo", "1": "1", "bar": "bar" }

Menggunakan array atau Mapobjek bisa menjadi cara yang lebih baik untuk mencapai ini. Mapberbagi beberapa kesamaan dengan Objectdan menjamin kunci untuk diulang sesuai urutan pemasangan , tanpa kecuali:

Kunci dalam Peta diurutkan sedangkan kunci yang ditambahkan ke objek tidak. Jadi, ketika iterasi di atasnya, objek Peta mengembalikan kunci dalam urutan penyisipan. (Perhatikan bahwa dalam ECMAScript 2015 objek spec tidak mempertahankan urutan pembuatan untuk string dan simbol Symbol, jadi traversal dari suatu objek dengan hanya kunci string yang akan menghasilkan kunci dalam urutan penyisipan)

Sebagai catatan, urutan properti pada objek tidak dijamin sama sekali sebelum ES2015. Definisi Objek dari ECMAScript Edisi Ketiga (pdf) :

4.3.3 Obyek

Objek adalah anggota dari tipe 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.

bpierre
sumber
Perilaku kunci integer tidak konsisten di semua browser. Beberapa browser yang lebih lama menggunakan kunci integer dalam urutan penyisipan (dengan kunci string) dan beberapa dalam urutan menaik.
Dave Dopson
1
@DaveDopson - Kanan - browser usang tidak mengikuti spesifikasi saat ini, karena mereka tidak diperbarui.
TJ Crowder
199

YA (untuk kunci non-integer).

Sebagian besar Peramban mengulangi properti objek sebagai:

  1. Kunci integer dalam urutan menaik (dan string seperti "1" yang diuraikan sebagai int)
  2. Kunci string, dalam urutan penyisipan (ES2015 menjamin ini dan semua browser mematuhi)
  3. Nama simbol, dalam urutan penyisipan (ES2015 menjamin ini dan semua browser mematuhi)

Beberapa browser lama menggabungkan kategori # 1 dan # 2, iterasi semua kunci dalam urutan penyisipan. Jika kunci Anda mungkin diuraikan sebagai bilangan bulat, yang terbaik adalah tidak bergantung pada urutan iterasi tertentu.

Urutan penyisipan Spec Bahasa Saat Ini (sejak ES2015) dipertahankan, kecuali dalam kasus kunci yang diuraikan sebagai bilangan bulat (misalnya "7" atau "99"), di mana perilaku bervariasi di antara browser. Sebagai contoh, Chrome / V8 tidak menghormati urutan penyisipan ketika kunci diuraikan sebagai angka.

Spesifikasi Bahasa Lama (sebelum ES2015) : Urutan Iterasi secara teknis tidak terdefinisi, tetapi semua browser utama mematuhi perilaku ES2015.

Perhatikan bahwa perilaku ES2015 adalah contoh yang baik dari spesifikasi bahasa yang didorong oleh perilaku yang ada, dan bukan sebaliknya. Untuk memahami lebih mendalam tentang pola pikir kompatibilitas-belakang itu, lihat http://code.google.com/p/v8/issues/detail?id=164 , bug Chrome yang mencakup secara detail keputusan desain di balik perilaku urutan iterasi Chrome. . Per salah satu (agak berpendapat) komentar pada laporan bug itu:

Standar selalu mengikuti implementasi, dari situlah XHR berasal, dan Google melakukan hal yang sama dengan mengimplementasikan Gears dan kemudian merangkul fungsionalitas HTML5 yang setara. Perbaikan yang tepat adalah memiliki ECMA secara resmi memasukkan perilaku standar de-facto ke putaran berikutnya dari spesifikasi.

Dave Dopson
sumber
2
@BenjaminGruenbaum - itu maksud saya sebenarnya. Pada 2014 semua vendor utama memiliki implementasi yang sama dan dengan demikian standar pada akhirnya akan mengikuti (yaitu, pada 2015).
Dave Dopson
2
Ngomong-ngomong: Bereaksi createFragmentAPI sudah bergantung pada ini ... 🤔
mik01aj
7
@BenjaminGruenbaum Komentar Anda salah. Dalam ES2015 pesanan dijamin hanya untuk metode yang dipilih . Lihat jawaban dari ftor bawah.
Piotr Dobrogost
82

Urutan properti di Objek normal adalah subjek kompleks dalam Javascript.

Sementara di ES5 secara eksplisit tidak ada pesanan yang ditentukan, ES2015 memiliki pesanan dalam kasus tertentu. Diberikan adalah objek berikut:

o = Object.create(null, {
  m: {value: function() {}, enumerable: true},
  "2": {value: "2", enumerable: true},
  "b": {value: "b", enumerable: true},
  0: {value: 0, enumerable: true},
  [Symbol()]: {value: "sym", enumerable: true},
  "1": {value: "1", enumerable: true},
  "a": {value: "a", enumerable: true},
});

Ini menghasilkan urutan berikut (dalam kasus tertentu):

Object {
  0: 0,
  1: "1",
  2: "2",
  b: "b",
  a: "a",
  m: function() {},
  Symbol(): "sym"
}
  1. kunci seperti integer dalam urutan menaik
  2. kunci normal dalam urutan penyisipan
  3. Simbol dalam urutan penyisipan

Jadi, ada tiga segmen, yang dapat mengubah urutan penyisipan (seperti yang terjadi dalam contoh). Dan kunci integer seperti tidak menempel pada urutan penyisipan sama sekali.

Pertanyaannya adalah, untuk metode apa pesanan ini dijamin dalam spesifikasi ES2015?

Metode berikut menjamin urutan yang ditunjukkan:

  • Object.assign
  • Object.defineProperties
  • Object.getOwnPropertyNames
  • Object.getOwnPropertySymbols
  • Reflect.ownKeys

Metode / loop berikut tidak menjamin pesanan sama sekali:

  • Objek.kunci
  • untuk..di
  • JSON.parse
  • JSON.stringify

Kesimpulan: Bahkan dalam ES2015 Anda tidak harus bergantung pada urutan properti objek normal di Javascript. Itu rentan terhadap kesalahan. Gunakan Mapsebagai gantinya.

kotak evolusi
sumber
Saya kira-kira menguji kesimpulan dalam simpul v8.11 dan itu benar.
merlin.ye
1
@BenjaminGruenbaum ada pemikiran?
evolutionxbox
Object.entries menjamin pesanan, mengikuti aturan integer
Mojimi
1
+1 untuk "Bahkan di ES2015 Anda tidak harus bergantung pada urutan properti objek normal di Javascript. Ini rentan terhadap kesalahan. Gunakan Peta sebagai gantinya.". Anda tidak dapat mengandalkan pesanan properti saat kepatuhan berbelit-belit. Bagaimana Anda bahkan menguji ini untuk komparabilitas mundur jika Anda harus mendukung browser yang lebih lama?
zero298
1
Apakah ada dokumentasi resmi tentang itu atau referensi?
Kalahkan
66

Pada saat penulisan, sebagian besar browser memang mengembalikan properti dalam urutan yang sama seperti yang dimasukkan, tetapi itu secara eksplisit tidak menjamin perilaku sehingga tidak seharusnya diandalkan.

The ECMAScript spesifikasi yang digunakan untuk mengatakan:

Mekanika dan urutan penghitungan properti ... tidak ditentukan.

Namun dalam ES2015 dan kemudian kunci non-integer akan dikembalikan dalam urutan penyisipan.

Alnitak
sumber
16
Chrome menerapkan urutan berbeda ke browser lain. Lihat code.google.com/p/v8/issues/detail?id=164
Tim Down
9
Opera 10.50 dan di atasnya, serta IE9, cocok dengan pesanan Chrome. Firefox dan Safari sekarang menjadi minoritas (dan keduanya juga menggunakan pesanan berbeda untuk Objects / Array).
gsnedders
1
@ Veverke secara eksplisit tidak ada jaminan pesanan, jadi orang harus selalu menganggap bahwa pesanan tersebut secara acak acak.
Alnitak
1
@Veverke Tidak, urutannya tidak bisa diprediksi. Ini tergantung pada implementasi dan dapat berubah setiap saat, dan dapat berubah (misalnya) setiap kali browser Anda memperbarui sendiri.
Alnitak
3
Jawaban ini salah dalam ES2015.
Benjamin Gruenbaum
42

Seluruh jawaban ini adalah dalam konteks kepatuhan spesifikasi, bukan apa yang dilakukan mesin pada saat tertentu atau secara historis.

Secara umum, tidak

Pertanyaan yang sebenarnya sangat kabur.

akankah properti berada dalam urutan yang sama seperti yang saya tambahkan

Dalam konteks apa?

Jawabannya adalah: itu tergantung pada sejumlah faktor. Secara umum, tidak .

Terkadang ya

Di sinilah Anda dapat mengandalkan pesanan kunci properti untuk polos Objects:

  • Mesin yang sesuai dengan ES2015
  • Properti sendiri
  • Object.getOwnPropertyNames(), Reflect.ownKeys(),Object.getOwnPropertySymbols(O)

Dalam semua kasus, metode ini mencakup kunci properti yang tidak dapat dihitung dan kunci pesanan seperti yang ditentukan oleh [[OwnPropertyKeys]](lihat di bawah). Mereka berbeda dalam jenis nilai kunci yang mereka sertakan ( Stringdan / atau Symbol). Dalam konteks ini Stringtermasuk nilai integer.

Object.getOwnPropertyNames(O)

Mengembalikan properti yang dikunci Osendiri String( nama properti ).

Reflect.ownKeys(O)

Mengembalikan properti Omilik sendiri Stringdan yang Symboldikunci.

Object.getOwnPropertySymbols(O)

Mengembalikan properti milik Osendiri yang Symboldikunci.

[[OwnPropertyKeys]]

Urutannya pada dasarnya: seperti integer Stringsdalam urutan menaik, tidak seperti integer Stringsdalam urutan pembuatan, Simbol dalam urutan penciptaan. Bergantung pada fungsi mana yang memanggil ini, beberapa tipe ini mungkin tidak termasuk.

Bahasa spesifiknya adalah bahwa kunci dikembalikan dalam urutan berikut:

  1. ... setiap tombol sendiri properti Pdari O[objek makhluk mengulangi] yang merupakan indeks integer, di urutan menaik indeks numerik

  2. ... setiap tombol properti sendiri Pdari Oitu adalah String tetapi tidak indeks integer, dalam rangka penciptaan properti

  3. ... setiap tombol properti sendiri Pdari Oitu adalah Symbol, dalam rangka penciptaan properti

Map

Jika Anda tertarik pada peta yang dipesan, Anda harus mempertimbangkan untuk menggunakan Mapjenis yang diperkenalkan di ES2015 alih-alih polos Objects.

JMM
sumber
13

Di browser modern, Anda dapat menggunakan Mapstruktur data alih-alih objek.

Pengembang mozilla> Peta

Objek Peta dapat mengulangi elemen-elemennya dalam urutan penyisipan ...

Topicus
sumber
7

Dalam ES2015, itu terjadi, tetapi tidak untuk apa yang mungkin Anda pikirkan

Urutan kunci dalam suatu objek tidak dijamin hingga ES2015. Itu didefinisikan implementasi.

Namun, dalam ES2015 di telah ditentukan. Seperti banyak hal dalam JavaScript, ini dilakukan untuk tujuan kompatibilitas dan umumnya mencerminkan standar tidak resmi yang ada di antara sebagian besar mesin JS (dengan Anda-tahu-siapa yang menjadi pengecualian).

Urutan didefinisikan dalam spesifikasi, di bawah operasi abstrak OrdinaryOwnPropertyKeys , yang menopang semua metode pengulangan pada kunci objek sendiri. Diparafrasekan, urutannya adalah sebagai berikut:

  1. Semua indeks integer kunci (hal-hal seperti "1123", "55", dll) di urutan menaik numerik.

  2. Semua kunci string yang bukan indeks bilangan bulat, sesuai urutan pembuatan (terlama-pertama).

  3. Semua kunci simbol, sesuai urutan pembuatan (terlama-pertama).

Adalah konyol untuk mengatakan bahwa pesanan itu tidak dapat diandalkan - itu dapat diandalkan, itu hanya mungkin bukan yang Anda inginkan, dan browser modern menerapkan pesanan ini dengan benar.

Beberapa pengecualian termasuk metode penghitungan kunci yang diwarisi, seperti for .. inloop. The for .. inLoop tidak menjamin agar sesuai dengan spesifikasi.

GregRos
sumber
7

Pada ES2015, pesanan properti dijamin untuk metode tertentu yang beralih ke properti. tapi tidak yang lain . Sayangnya, metode yang tidak dijamin memiliki pesanan umumnya yang paling sering digunakan:

  • Object.keys, Object.values,Object.entries
  • for..in loop
  • JSON.stringify

Tetapi, pada ES2020, pesanan properti untuk metode - metode yang sebelumnya tidak dapat dipercaya ini akan dijamin oleh spesifikasi untuk diulangi dengan cara deterministik yang sama seperti yang lain, karena proposal yang sudah selesai : for-in mechanical .

Sama seperti dengan metode yang memiliki urutan iterasi dijamin (seperti Reflect.ownKeysdan Object.getOwnPropertyNames), metode yang sebelumnya tidak ditentukan juga akan beralih dalam urutan berikut:

  • Tombol array numerik, dalam urutan numerik
  • Semua kunci non-Simbol lainnya, dalam urutan penyisipan
  • Kunci simbol, dalam urutan penyisipan

Inilah yang hampir setiap implementasi sudah (dan telah dilakukan selama bertahun-tahun), tetapi proposal baru telah membuatnya resmi.

Meskipun spesifikasi saat ini menyisakan ... dalam urutan iterasi " hampir sama sekali tidak ditentukan , mesin nyata cenderung lebih konsisten:"

Kurangnya spesifisitas dalam ECMA-262 tidak mencerminkan kenyataan. Dalam diskusi bertahun-tahun yang lalu, implementor telah mengamati bahwa ada beberapa kendala pada perilaku for-in yang harus diikuti oleh siapa saja yang ingin menjalankan kode di web.

Karena setiap implementasi sudah berulang properti lebih dapat diprediksi, dapat dimasukkan ke dalam spesifikasi tanpa melanggar kompatibilitas ke belakang.


Ada beberapa kasus aneh yang implementasi saat ini tidak setuju, dan dalam kasus seperti itu, pesanan yang dihasilkan akan terus tidak ditentukan. Untuk pemesanan properti dijamin :

Baik objek yang di-iterasi atau apapun dalam rantai prototipe adalah proxy, array yang diketik, objek namespace modul, atau host objek eksotis.

Baik objek maupun apapun dalam rantai prototipe memiliki perubahan prototipe selama iterasi.

Baik objek maupun apapun dalam rantai prototipe memiliki properti yang dihapus selama iterasi.

Tidak ada dalam rantai prototipe objek yang memiliki properti yang ditambahkan selama iterasi.

Tidak ada properti objek atau apapun dalam rantai prototipe yang memiliki perubahan enumerability selama iterasi.

Tidak ada properti non-enumerable membayangi yang enumerable.

Performa Tertentu
sumber
5

Seperti yang telah dinyatakan orang lain, Anda tidak memiliki jaminan tentang urutan ketika Anda beralih ke properti suatu objek. Jika Anda memerlukan daftar beberapa bidang yang dipesan, saya sarankan untuk membuat array objek.

var myarr = [{somfield1: 'x', somefield2: 'y'},
{somfield1: 'a', somefield2: 'b'},
{somfield1: 'i', somefield2: 'j'}];

Dengan cara ini Anda dapat menggunakan loop reguler untuk dan memiliki urutan insert. Anda kemudian dapat menggunakan metode semacam Array untuk mengurutkan ini ke dalam array baru jika diperlukan.

Drew Durham
sumber
3

Hanya menemukan ini dengan cara yang sulit.

Menggunakan Bereaksi dengan Redux, wadah negara kunci mana yang ingin saya lintasi untuk menghasilkan anak-anak disegarkan setiap kali toko diubah (sesuai konsep kekekalan Redux).

Jadi, untuk mengambil Object.keys(valueFromStore)saya menggunakan Object.keys(valueFromStore).sort(), sehingga saya setidaknya sekarang memiliki urutan abjad untuk kunci.

keris
sumber
-7

Dari standar JSON :

Objek adalah kumpulan unordered dari nol atau lebih pasangan nama / nilai, di mana nama adalah string dan nilai adalah string, angka, boolean, null, objek, atau array.

(penekanan milikku).

Jadi, tidak, Anda tidak dapat menjamin pesanan.

Kevin Lacquement
sumber
7
ini ditentukan oleh standar ECMAScript - bukan spesifikasi JSON.
Alnitak
7
@Alnitak, @Iacqui: JSON hanya mengambil ini dari spesifikasi ECMAScript. Ini ditentukan untuk JSON juga, tetapi ini tidak benar-benar berhubungan dengan pertanyaan.
PaĹ­lo Ebermann