JavaScript Nesne Özellik Siparişini Garantiler mi?


647

Eğer böyle bir nesne oluşturursam:

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

Ortaya çıkan nesne her zaman böyle mi görünecek?

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

Yani, özellikler onları eklediğim sırayla mı olacak?





1
@TJCrowder Kabul edilen cevabın neden artık doğru olmadığına dair biraz daha ayrıntı verebilir misiniz? Bağladığınız soru, mülk siparişinin şartname başına hala garanti edilmediği fikrine kaynıyor gibi görünüyor.
zero298

2
@ zero298: Bu soruya kabul edilen cevap , ES2015 + 'dan itibaren belirtilen mülk sırasını açıkça tanımlamaktadır . Eski işlemleri ( for-in, Object.keys) (resmi olarak) bunu destekleyecek gerekmez, ama orada olduğunu sipariş ver. (Gayri resmi olarak: Firefox, Chrome ve Edge, for-in ve Object.key'lerde bile resmi olarak gerekli olmadıkları belirtilen sırayı izler: jsfiddle.net/arhbn3k2/1 )
TJ Crowder

Yanıtlar:


474

Nesneler için yineleme sırası, ES2015'ten bu yana belirli bir kurallar dizisini izler, ancak ekleme sırasını (her zaman) izlemez . Basitçe söylemek gerekirse, yineleme sırası, dizeler anahtarları için ekleme sırasının ve sayı benzeri anahtarlar için artan siparişin bir kombinasyonudur:

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

Bir dizi veya Mapnesne kullanmak bunu başarmanın daha iyi bir yolu olabilir. istisnasız olarak ekleme sırasına göre yinelenecek anahtarlarlaMap bazı benzerlikleri paylaşır Objectve bunları garanti eder :

Haritadaki anahtarlar sıralanırken, nesneye eklenen anahtarlar sıralanmaz. Böylece, yinelendiğinde, bir Map nesnesi anahtarları ekleme sırasına göre döndürür. (ECMAScript 2015 spesifikasyon nesnelerinin, dize ve Sembol anahtarları için oluşturma sırasını koruduğunu unutmayın; bu nedenle, yalnızca dize anahtarlarıyla bir nesnenin geçişi, ekleme sırasına göre anahtarlar verir)

Bir not olarak, ES2015'ten önce nesnelerdeki özellik sırası hiç garanti edilmedi. ECMAScript Third Edition'dan bir nesnenin tanımı (pdf) :

4.3.3 Nesne

Bir nesne, Object türünün bir üyesidir. Her biri ilkel bir değer, nesne veya işlev içeren sıralanmamış bir özellik koleksiyonudur . Bir nesnenin özelliğinde saklanan işleve yöntem denir.


Tamsayı tuşlarının davranışı tüm tarayıcılarda tutarlı değildir. Bazı eski tarayıcılar tamsayı tuşlarını ekleme sırasında (dize tuşları ile) ve bazıları artan sırada yineler.
Dave Dopson

1
@DaveDopson - Sağ - eski tarayıcılar güncellenmediği için mevcut spesifikasyona uymuyor.
TJ Crowder

199

EVET (tamsayı olmayan tuşlar için).

Çoğu Tarayıcı nesne özelliklerini şu şekilde yineler:

  1. Tamsayı tuşları artan sırada (ve "1" gibi dizeler ayrıştırılıyor)
  2. Ekleme sırasında dize tuşları (ES2015 bunu garanti eder ve tüm tarayıcılar uyumludur)
  3. Ekleme sırasında simge adları (ES2015 bunu garanti eder ve tüm tarayıcılar uyumludur)

Bazı eski tarayıcılar # 1 ve # 2 kategorilerini birleştirerek tüm anahtarları ekleme sırasında yineler. Anahtarlarınız tamsayı olarak ayrışabilirse, herhangi bir özel yineleme sırasına güvenmemek en iyisidir.

Geçerli Dil Spesifikasyonu (ES2015'ten beri) , tamsayı olarak ayrışan anahtarlar (ör. "7" veya "99") dışında, davranışın tarayıcılar arasında değiştiği durumlar hariç, ekleme sırası korunur. Örneğin, tuşlar sayısal olarak ayrıştırıldığında Chrome / V8 ekleme sırasına uymaz.

Eski Dil Özellikleri (ES2015'ten önce) : Yineleme sırası teknik olarak tanımlanmamıştı, ancak tüm büyük tarayıcılar ES2015 davranışına uyuyordu.

ES2015 davranışının, varolan davranış tarafından yönlendirilen dil spesifikasyonunun iyi bir örneği olduğunu ve bunun tersi olmadığını unutmayın. Bu geriye dönük uyumluluk anlayışını daha iyi anlamak için Chrome'un yineleme sırası davranışının arkasındaki tasarım kararlarını ayrıntılı bir şekilde kapsayan bir Chrome hatası olan http://code.google.com/p/v8/issues/detail?id=164 adresine bakın. . Bu hata raporundaki (daha çok görüşlü) yorumlardan biri başına:

Standartlar her zaman uygulamaları takip eder, bu XHR'nin geldiği yerdir ve Google aynı şeyi Gears'ı uygulayıp eşdeğer HTML5 işlevselliğini kucaklayarak yapar. Doğru çözüm ECMA'nın fiili standart davranışı spesifikasyonun bir sonraki devrine resmen dahil etmesini sağlamaktır.


2
@BenjaminGruenbaum - bu tam olarak benim açımdan. 2014 itibariyle tüm büyük satıcıların ortak bir uygulaması vardı ve bu nedenle standartlar nihayetinde uygulanacak (yani 2015'te).
Dave Dopson

2
Bu arada: React createFragmentAPI zaten buna güveniyor ... 🤔
mik01aj

7
@BenjaminGruenbaum Yorumunuz yanlış. ES2015'te sipariş yalnızca seçilen yöntemler için garanti edilir . Bkz cevabı ait ftor altında.
Piotr Dobrogost

82

Normal Nesnelerdeki özellik sırası Javascript'te karmaşık bir konudur.

ES5'te açıkça bir emir belirtilmemiş olmakla birlikte, ES2015'in bazı durumlarda bir emri vardır. Verilen aşağıdaki nesnedir:

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

Bu, aşağıdaki sıra ile sonuçlanır (bazı durumlarda):

Object {
  0: 0,
  1: "1",
  2: "2",
  b: "b",
  a: "a",
  m: function() {},
  Symbol(): "sym"
}
  1. artan sırada tamsayı benzeri tuşlar
  2. ekleme sırasında normal anahtarlar
  3. Kampanya siparişindeki simgeler

Bu nedenle, ekleme sırasını değiştirebilecek üç örnek vardır (örnekte olduğu gibi). Tamsayı benzeri tuşlar ekleme sırasına hiç uymaz.

Soru, bu siparişin ES2015 spesifikasyonunda hangi yöntemler için garanti edildiği?

Aşağıdaki yöntemler gösterilen sırayı garanti eder:

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

Aşağıdaki yöntemler / döngüler hiçbir sipariş garantisi vermez:

  • Object.keys
  • for..in
  • JSON.parse
  • JSON.stringify

Sonuç: ES2015'te bile Javascript'teki normal nesnelerin özellik sırasına güvenmemelisiniz. Hatalara yatkındır. MapBunun yerine kullanın .


V8.11 düğümündeki sonucu kabaca test ettim ve doğrudur.
merlin.ye

1
@BenjaminGruenbaum düşünceniz var mı?
evolutionxbox

Tamsayı kural aşağıdaki Object.entries garanti düzeni,
Mojimi

1
+1 için "ES2015'te bile Javascript'teki normal nesnelerin özellik sırasına güvenmemelisiniz. Hatalara eğilimlidir. Bunun yerine Harita'yı kullanın.". Uyumluluk katlandığında mülk siparişine güvenemezsiniz. Eski tarayıcıları desteklemeniz gerekirse bunu geriye doğru karşılaştırılabilirlik açısından nasıl test edersiniz?
zero298

1
Bununla ilgili resmi bir belge veya referans var mı?
yendi

66

Yazma sırasında, çoğu tarayıcı özellikleri eklendikleri sırayla döndürdü, ancak açıkça garanti edilmedi, bu yüzden güvenilmemeliydi.

ECMAScript şartname şöyle derdi:

Mekaniklerin ve özelliklerin numaralandırılma sırası ... belirtilmemiştir.

Ancak ES2015 ve sonraki sürümlerde tamsayı olmayan anahtarlar ekleme sırasında döndürülür.


16
Chrome, diğer tarayıcılara farklı bir sipariş verir. Bkz. Code.google.com/p/v8/issues/detail?id=164
Tim Down

9
Opera 10.50 ve üstü ve IE9, Chrome'un siparişiyle eşleşiyor. Firefox ve Safari artık bir azınlık (ve her ikisi de Nesneler / Diziler için farklı siparişler kullanıyor).
gsnedders

1
@Veverke açıkça sipariş üzerinde hiçbir garanti yoktur, bu yüzden her zaman siparişin etkili bir şekilde rastgele olduğunu varsayalım.
Alnitak

1
@Veverke Hayır, emir tahmin edilebilir bir şey değil. Uygulamaya bağlıdır ve herhangi bir zamanda değişebilir ve tarayıcınızın kendisini her güncellediğinde (örneğin) değişebilir.
Alnitak

3
Bu yanıt ES2015'te yanlıştır.
Benjamin Gruenbaum

42

Bu cevabın tamamı, herhangi bir motorun belirli bir anda veya tarihsel olarak ne yaptığını değil, spesifik uygunluk bağlamındadır.

Genellikle hayır

Asıl soru çok belirsiz.

özellikler onları eklediğim sırayla olacak mı

Hangi bağlamda?

Cevap: bir dizi faktöre bağlıdır. Genel olarak, hayır .

Bazen evet

Plain için özellik anahtarı sırasına güvenebileceğiniz yer Objects:

  • ES2015 uyumlu motor
  • Kendi mülkler
  • Object.getOwnPropertyNames(), Reflect.ownKeys(),Object.getOwnPropertySymbols(O)

Her durumda, bu yöntemler numaralandırılamayan özellik anahtarlarını ve tarafından belirtildiği gibi sipariş anahtarlarını içerir [[OwnPropertyKeys]](aşağıya bakın). İçerdikleri ( Stringve / veya Symbol) anahtar değerlerin türüne göre farklılık gösterirler . Bu bağlamda Stringtamsayı değerleri içerir.

Object.getOwnPropertyNames(O)

OKendi Stringanahtarlı özelliklerini ( özellik adları ) döndürür .

Reflect.ownKeys(O)

Kendine Oait Stringve Symbolanahtarlı özelliklerini döndürür .

Object.getOwnPropertySymbols(O)

OKendi Symbolanahtarlı özelliklerini döndürür .

[[OwnPropertyKeys]]

Sıralama esastır: Stringsartan sırada tamsayıya benzer Strings, oluşturma sırasında tamsayıya benzer , oluşturma sırasındaki simgeler. Hangi işlevin bunu başlattığına bağlı olarak, bu türlerin bazıları dahil edilmeyebilir.

Özel dil, anahtarların aşağıdaki sırada döndürülmesidir:

  1. ... artan sayısal dizin sırasına göre P, O[yinelenen nesnenin] bir tamsayı dizini olan her bir özellik anahtarı

  2. ... Her Kendi mülkiyet anahtar Pait Obir dize ancak mülkiyet oluşturma amacıyla, bir tamsayı indeksi değildir

  3. ... her bir özellik anahtarı P, Oözellik oluşturma sırasında bir Sembol'tür

Map

Sıralı haritalarla ilgileniyorsanız Map, ES2015'te sunulan türü düz yerine kullanmayı düşünmelisiniz Objects.



7

ES2015'te öyle, ama ne düşündüğünüze göre değil

Bir nesnedeki anahtarların sırası, ES2015'e kadar garanti edilmedi. Uygulama tanımlıydı.

Ancak, ES2015 yılında belirtildi. JavaScript'teki pek çok şey gibi, bu da uyumluluk amacıyla yapıldı ve genellikle çoğu JS motoru arasında mevcut bir gayri resmi standardı yansıttı (bildiğiniz bir istisna).

Sipariş, bir nesnenin kendi anahtarları üzerinden tüm yineleme yöntemlerini destekleyen OrdinaryOwnPropertyKeys soyut işlemi altında spesifikasyonda tanımlanır . Başka deyişle, sıra aşağıdaki gibidir:

  1. Tüm tamsayı endeksi tuşları (gibi şeyler "1123", "55"sayısal artan düzende, vs).

  2. Oluşturma sırasına göre (en eskiden önce) tamsayı dizini olmayan tüm dize anahtarları.

  3. Tüm sembol tuşları, oluşturulma sırasına göre (en eskiden).

Siparişin güvenilir olmadığını söylemek saçmadır - güvenilirdir, muhtemelen istediğiniz şey değildir ve modern tarayıcılar bu siparişi doğru bir şekilde uygular.

Bazı istisnalar, for .. indöngü gibi devralınmış anahtarları numaralandırma yöntemlerini içerir . for .. inDöngü şartnamesine uygun sırada garanti etmez.


7

ES2015 itibariyle, özellikler üzerinde yineleme yapan belirli yöntemler için özellik sırası garanti edilir. ama diğerleri değil . Ne yazık ki, siparişi vermesi garanti edilmeyen yöntemler genellikle en sık kullanılanlardır:

  • Object.keys, Object.values,Object.entries
  • for..in döngüler
  • JSON.stringify

Ama, ES2020 itibariyle önceleri güvenilmez yöntemler için ürünlerin sipariş edecek şartname tarafından garanti edilmesi nedeniyle etmek, diğerleriyle aynı deterministik şekilde iterated edilecek bitmiş önerisi: for-in mekaniği .

Tıpkı garantili yineleme sırasına ( Reflect.ownKeysve gibi Object.getOwnPropertyNames) sahip yöntemlerde olduğu gibi , daha önce belirtilmemiş yöntemler de aşağıdaki sırayla yinelenir :

  • Sayısal dizi tuşları, artan sayısal sırada
  • Ekleme sırasında tüm diğer Sembol olmayan tuşlar
  • Ekleme sırasında simge tuşları

Her uygulamanın halihazırda yaptığı (ve yıllardır yaptığı) budur, ancak yeni teklif resmi hale getirmiştir.

Her ne kadar mevcut şartname yineleme sırasında ayrılsa da " neredeyse tamamen belirtilmemiş , gerçek motorlar daha tutarlı olma eğilimindedir:"

ECMA-262'de özgüllük eksikliği gerçeği yansıtmaz. Yıllar öncesindeki tartışmada, uygulayıcılar, web'de kod çalıştırmak isteyen herkesin izlemesi gereken for-in davranışında bazı kısıtlamalar olduğunu gözlemlediler.

Her uygulama önceden tahmin edilebileceği gibi özellikleri yinelediğinden, geriye dönük uyumluluğu bozmadan spesifikasyona dahil edilebilir.


Şu anda uygulamaların kabul etmediği birkaç garip durum var ve bu gibi durumlarda ortaya çıkan sipariş belirtilmeden devam edecek. Mülk siparişinin garanti altına alınması için :

Ne yinelenen nesne ne de prototip zincirindeki hiçbir şey bir proxy, yazılan dizi, modül ad alanı nesnesi veya ana bilgisayar egzotik nesnesidir.

Ne nesne, ne de prototip zincirindeki hiçbir şey, yineleme sırasında prototip değişikliğine sahip değildir.

Ne nesne ne de prototip zincirindeki hiçbir şey yineleme sırasında silinmiş bir özelliğe sahip değildir.

Nesnenin prototip zincirindeki hiçbir şeyin yineleme sırasında eklenen bir özelliği yoktur.

Nesnenin hiçbir özelliği veya prototip zincirindeki hiçbir şey yineleme sırasında numaralandırılabilirlik değişikliğine sahip değildir.

Numaralandırılamayan hiçbir özellik numaralandırılamaz bir özelliği gölgelemez.


5

Diğerlerinin belirttiği gibi, bir nesnenin özelliklerini yinelediğinizde sipariş için herhangi bir garantiniz yoktur. Birden fazla alanın sıralı bir listesine ihtiyacınız varsa, bir nesne dizisi oluşturmanızı önerdim.

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

Bu şekilde döngü için bir normal kullanabilir ve ekleme sırası alabilirsiniz. Daha sonra, gerekirse bunu yeni bir diziye sıralamak için Array sıralama yöntemini kullanabilirsiniz.


3

Sadece bunu zor yoldan öğrendim.

Çocuk üretmek için geçiş yapmak istediğim anahtarları Redux ile React'i kullanarak, mağaza her değiştiğinde (Redux'un değişmezlik kavramlarına göre) yenilenir.

Böylece, almak için Object.keys(valueFromStore)kullandım Object.keys(valueFromStore).sort(), böylece en azından şimdi anahtarlar için alfabetik bir düzenim var.


-7

Gönderen JSON standardına :

Nesne, bir adın bir dize ve bir değerin bir dize, sayı, boole, null, nesne veya dizi olduğu sıfır veya daha fazla ad / değer çiftinin sırasız bir koleksiyonudur.

(benimkini vurgulayın).

Yani, siparişi garanti edemezsiniz.


7
bu ECMAScript standardı tarafından belirtilir - JSON spesifikasyonu tarafından değil.
Alnitak

7
@Alnitak, @Iacqui: JSON bunu yalnızca ECMAScript spesifikasyonundan alır. JSON için de belirtilmiştir, ancak bu gerçekten soruyla ilgili değildir.
Paŭlo Ebermann
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.