JavaScript garantisce l'oggetto della proprietà dell'oggetto?


647

Se creo un oggetto come questo:

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

L'oggetto risultante sarà sempre così?

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

Cioè, le proprietà saranno nello stesso ordine in cui le ho aggiunte?





1
@TJCrowder Potresti entrare in qualche dettaglio in più sul perché la risposta accettata non è più accurata? La domanda che hai collegato sembra ridursi all'idea che l'ordine di proprietà non è ancora garantito per specifica.
zero298

2
@ zero298: la risposta accettata a quella domanda descrive chiaramente l' ordine di proprietà specificato a partire da ES2015 +. Le operazioni legacy ( for-in, Object.keys) non devono supportarlo (ufficialmente), ma ora c'è ordine. (Ufficiosamente: Firefox, Chrome e Edge seguono tutti l'ordine specificato anche in for-in e Object.keys, dove non sono ufficialmente tenuti a: jsfiddle.net/arhbn3k2/1 )
TJ Crowder

Risposte:


474

L'ordine di iterazione per gli oggetti segue un certo insieme di regole da ES2015, ma non segue (sempre) l'ordine di inserimento . In poche parole, l'ordine di iterazione è una combinazione dell'ordine di inserimento per le chiavi delle stringhe e dell'ordine crescente per le chiavi simili a numeri:

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

L'uso di un array o un Mapoggetto può essere un modo migliore per raggiungere questo obiettivo. Mapcondivide alcune somiglianze con Objecte garantisce che le chiavi vengano iterate in ordine di inserimento , senza eccezioni:

Le chiavi in ​​Mappa sono ordinate mentre le chiavi aggiunte all'oggetto non lo sono. Pertanto, quando si scorre su di esso, un oggetto Mappa restituisce le chiavi in ​​ordine di inserimento. (Si noti che nell'oggetto ECMAScript 2015 gli oggetti spec conservano l'ordine di creazione per chiavi stringa e simboli, quindi l'attraversamento di un oggetto con cioè solo chiavi stringa produrrebbe le chiavi in ​​ordine di inserimento)

Come nota, l'ordine delle proprietà negli oggetti non era affatto garantito prima di ES2015. Definizione di un oggetto da ECMAScript Third Edition (pdf) :

4.3.3 Oggetto

Un oggetto è un membro del tipo Object. È una raccolta non ordinata di proprietà, ognuna delle quali contiene un valore, un oggetto o una funzione primitivi. Una funzione memorizzata in una proprietà di un oggetto è chiamata metodo.


Il comportamento delle chiavi intere non è coerente in tutti i browser. Alcuni browser meno recenti eseguono l'iterazione di chiavi intere in ordine di inserimento (con le chiavi stringa) e altre in ordine crescente.
Dave Dopson,

1
@DaveDopson - Giusto - i browser obsoleti non seguono le specifiche correnti, perché non sono aggiornati.
TJ Crowder

199

SÌ (per chiavi non intere).

La maggior parte dei browser itera le proprietà degli oggetti come:

  1. Chiavi intere in ordine crescente (e stringhe come "1" che analizzano come ints)
  2. Chiavi stringa, in ordine di inserimento (ES2015 garantisce questo e tutti i browser sono conformi)
  3. Nomi dei simboli, in ordine di inserimento (ES2015 garantisce questo e tutti i browser sono conformi)

Alcuni browser meno recenti combinano le categorie n. 1 e n. 2, ripetendo tutte le chiavi in ​​ordine di inserimento. Se le tue chiavi potrebbero essere analizzate come numeri interi, è meglio non fare affidamento su alcun ordine di iterazione specifico.

L' ordine di inserimento delle specifiche della lingua corrente (dal ES2015) viene conservato, tranne nel caso di chiavi che analizzano come numeri interi (ad esempio "7" o "99"), in cui il comportamento varia tra i browser. Ad esempio, Chrome / V8 non rispetta l'ordine di inserimento quando i tasti vengono analizzati come numerici.

Specifiche della vecchia lingua (prima di ES2015) : l'ordine di iterazione era tecnicamente indefinito, ma tutti i principali browser rispettavano il comportamento ES2015.

Si noti che il comportamento ES2015 è stato un buon esempio delle specifiche del linguaggio guidate dal comportamento esistente e non viceversa. Per avere un'idea più approfondita di quella mentalità di retrocompatibilità, consulta http://code.google.com/p/v8/issues/detail?id=164 , un bug di Chrome che copre in dettaglio le decisioni di progettazione alla base del comportamento dell'ordine di iterazione di Chrome . Per uno dei commenti (piuttosto supponente) su quel bug report:

Gli standard seguono sempre le implementazioni, ecco da dove proviene XHR e Google fa la stessa cosa implementando Gears e adottando funzionalità HTML5 equivalenti. La soluzione giusta è che l'ECMA incorpori formalmente il comportamento standard di fatto nel prossimo giro di specifiche.


2
@BenjaminGruenbaum - quello era esattamente il mio punto. A partire dal 2014 tutti i principali distributori avevano un'implementazione comune e quindi gli standard seguiranno alla fine (vale a dire, nel 2015).
Dave Dopson,

2
A proposito: React createFragmentAPI si basa già su questo ... 🤔
mik01aj

7
@BenjaminGruenbaum Il tuo commento è falso. In ES2015 l'ordine è garantito solo per i metodi selezionati . Vedi la risposta di Ftor di seguito.
Piotr Dobrogost,

84

L'ordine delle proprietà negli oggetti normali è un argomento complesso in Javascript.

Mentre in ES5 esplicitamente non è stato specificato alcun ordine, ES2015 ha un ordine in alcuni casi. Dato è il seguente oggetto:

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

Ciò si traduce nel seguente ordine (in alcuni casi):

Object {
  0: 0,
  1: "1",
  2: "2",
  b: "b",
  a: "a",
  m: function() {},
  Symbol(): "sym"
}
  1. chiavi di tipo intero in ordine crescente
  2. chiavi normali in ordine di inserimento
  3. Simboli in ordine di inserimento

Pertanto, ci sono tre segmenti, che possono alterare l'ordine di inserimento (come è successo nell'esempio). E le chiavi di tipo intero non si attengono affatto all'ordine di inserimento.

La domanda è: per quali metodi questo ordine è garantito nelle specifiche ES2015?

I seguenti metodi garantiscono l'ordine mostrato:

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

I seguenti metodi / loop non garantiscono alcun ordine:

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

Conclusione: anche in ES2015 non dovresti fare affidamento sull'ordine delle proprietà degli oggetti normali in Javascript. È soggetto a errori. Usa Mapinvece.


Ho testato approssimativamente la conclusione nel nodo v8.11 ed è corretta.
merlin.ye,

1
@BenjaminGruenbaum qualche pensiero?
evolutionxbox,

Object.entries garantisce l'ordine, seguendo la regola intera
Mojimi

1
+1 per "Anche in ES2015 non dovresti fare affidamento sull'ordine delle proprietà degli oggetti normali in Javascript. È soggetto a errori. Usa invece Mappa.". Non è possibile fare affidamento sull'ordine di proprietà quando la conformità è contorta. Come si può testare per questo per la comparabilità all'indietro se è necessario supportare i browser più vecchi?
zero298,

1
Esiste una documentazione ufficiale al riguardo o un riferimento?
Batti il

66

Al momento della scrittura, la maggior parte dei browser ha restituito le proprietà nello stesso ordine in cui sono state inserite, ma non è stato esplicitamente garantito il comportamento, quindi non avrebbe dovuto essere invocato.

La specifica ECMAScript diceva:

La meccanica e l'ordine di enumerazione delle proprietà ... non sono specificati.

Tuttavia, in ES2015 e successive le chiavi non intere verranno restituite nell'ordine di inserimento.


16
Chrome implementa un ordine diverso rispetto ad altri browser. Vedi code.google.com/p/v8/issues/detail?id=164
Tim Down

9
Opera 10.50 e versioni successive, nonché IE9, corrispondono all'ordine di Chrome. Firefox e Safari sono ora una minoranza (ed entrambi usano anche ordini diversi per Oggetti / Matrici).
gsnedders

1
@Veverke non c'è esplicitamente alcuna garanzia sull'ordine, quindi si dovrebbe sempre presumere che l'ordine sia effettivamente casuale.
Alnitak,

1
@Veverke No, l'ordine non è affatto prevedibile. È dipendente dall'implementazione e soggetto a modifiche in qualsiasi momento e potrebbe cambiare (ad esempio) ogni volta che il browser si aggiorna da solo.
Alnitak,

3
Questa risposta è falsa in ES2015.
Benjamin Gruenbaum,

42

Tutta questa risposta è nel contesto della conformità alle specifiche, non quello che fa qualsiasi motore in un determinato momento o storicamente.

Generalmente no

La vera domanda è molto vaga.

le proprietà saranno nello stesso ordine in cui le ho aggiunte

In quale contesto?

La risposta è: dipende da una serie di fattori. In generale, no .

A Volte si

Qui puoi contare sull'ordine delle chiavi di proprietà per plain Objects:

  • Motore conforme a ES2015
  • Proprietà proprie
  • Object.getOwnPropertyNames(), Reflect.ownKeys(),Object.getOwnPropertySymbols(O)

In tutti i casi questi metodi includono chiavi di proprietà non enumerabili e chiavi di ordine come specificato da [[OwnPropertyKeys]](vedi sotto). Differiscono nel tipo di valori chiave che includono ( Stringe / o Symbol). In questo contesto Stringinclude valori interi.

Object.getOwnPropertyNames(O)

Restituisce Ole proprietà con Stringchiave propria ( nomi delle proprietà ).

Reflect.ownKeys(O)

Restituisce Ole proprietà proprie Stringe con Symbolchiave.

Object.getOwnPropertySymbols(O)

Restituisce Ole proprietà con Symbolchiave propria .

[[OwnPropertyKeys]]

L'ordine è essenzialmente: intero Stringsin ordine crescente, non intero Stringsin ordine di creazione, simboli in ordine di creazione. A seconda della funzione che lo richiama, alcuni di questi tipi potrebbero non essere inclusi.

La lingua specifica è che le chiavi vengono restituite nel seguente ordine:

  1. ... ogni propria chiave Pdi proprietà di O[l'oggetto che viene iterato] che è un indice intero, in ordine di indice numerico crescente

  2. ... ogni propria chiave Pdi proprietà Oè una stringa ma non è un indice intero, nell'ordine di creazione della proprietà

  3. ... ogni chiave Pdi proprietà di Oquesto è un simbolo, nell'ordine di creazione della proprietà

Map

Se sei interessato alle mappe ordinate, dovresti prendere in considerazione l'uso del Maptipo introdotto in ES2015 invece che semplice Objects.


13

Nei browser moderni è possibile utilizzare la Mapstruttura dei dati anziché un oggetto.

Sviluppatore mozilla> Mappa

Un oggetto Mappa può iterare i suoi elementi in ordine di inserimento ...


7

In ES2015, lo fa, ma non a quello che potresti pensare

L'ordine delle chiavi in ​​un oggetto non è stato garantito fino a ES2015. È stato definito dall'implementazione.

Tuttavia, in ES2015 è stato specificato. Come molte altre cose in JavaScript, questo è stato fatto per motivi di compatibilità e generalmente rifletteva uno standard non ufficiale esistente nella maggior parte dei motori JS (con te-sai-chi è un'eccezione).

L'ordine è definito nelle specifiche, sotto l'operazione astratta OrdinaryOwnPropertyKeys , che sostiene tutti i metodi di iterazione sulle stesse chiavi di un oggetto. Parafrasato, l'ordine è il seguente:

  1. Tutti indice intero tasti (cose come "1123", "55"ecc) in ordine numerico crescente.

  2. Tutte le chiavi di stringa che non sono indici interi, in ordine di creazione (prima i più vecchi).

  3. Tutte le chiavi dei simboli, in ordine di creazione (prima i più vecchi).

È sciocco dire che l'ordine è inaffidabile: è affidabile, probabilmente non è quello che desideri e i browser moderni implementano correttamente questo ordine.

Alcune eccezioni includono metodi per enumerare le chiavi ereditate, come il for .. inciclo. Il for .. inloop non garantisce l'ordine in base alle specifiche.


7

A partire da ES2015, l'ordine delle proprietà è garantito per alcuni metodi che ripetono le proprietà. ma non altri . Sfortunatamente, i metodi che non sono garantiti per avere un ordine sono generalmente i più usati:

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

Ma, a partire da ES2020, l'ordine di proprietà per questi metodi precedentemente non affidabili sarà garantito dalle specifiche per essere ripetute nello stesso modo deterministico degli altri, a causa della proposta finita : meccanica for-in .

Proprio come con i metodi che hanno un ordine di iterazione garantito (come Reflect.ownKeyse Object.getOwnPropertyNames), anche i metodi precedentemente non specificati eseguiranno l'iterazione nel seguente ordine:

  • Tasti numerici dell'array, in ordine numerico crescente
  • Tutte le altre chiavi non simboliche, in ordine di inserimento
  • Tasti simbolo, in ordine di inserimento

Questo è praticamente ciò che ogni implementazione già fa (e ha fatto per molti anni), ma la nuova proposta l'ha resa ufficiale.

Sebbene le attuali specifiche lasciano .. nell'ordine di iterazione " quasi totalmente non specificato , i motori reali tendono ad essere più coerenti:"

La mancanza di specificità nell'ECMA-262 non riflette la realtà. In una discussione che risale a anni fa, gli implementatori hanno osservato che ci sono alcuni vincoli sul comportamento del for-in che chiunque voglia eseguire codice sul web deve seguire.

Poiché ogni implementazione scorre già prevedibilmente sulle proprietà, può essere inserita nelle specifiche senza interrompere la compatibilità con le versioni precedenti.


Esistono alcuni casi strani su cui le implementazioni attualmente non concordano e, in tali casi, l'ordine risultante continuerà a non essere specificato. Per garantire l' ordine della proprietà :

Né l'oggetto che viene iterato né alcunché nella sua catena di prototipi è un proxy, un array tipizzato, un oggetto dello spazio dei nomi del modulo o un oggetto esotico host.

Né l'oggetto né alcunché nella sua catena di prototipi ha il suo prototipo cambiato durante l'iterazione.

Né l'oggetto né alcunché nella sua catena di prototipi ha una proprietà eliminata durante l'iterazione.

Nulla nella catena di prototipi dell'oggetto ha una proprietà aggiunta durante l'iterazione.

Nessuna proprietà dell'oggetto o qualsiasi cosa nella sua catena di prototipi ha la sua modifica dell'enumerabilità durante l'iterazione.

Nessuna proprietà non enumerabile ne ombreggia una enumerabile.


5

Come altri hanno già detto, non hai alcuna garanzia riguardo all'ordine quando passi le proprietà di un oggetto. Se hai bisogno di un elenco ordinato di più campi, ho suggerito di creare una matrice di oggetti.

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

In questo modo puoi usare un normale ciclo per e avere l'ordine di inserimento. È quindi possibile utilizzare il metodo di ordinamento degli array per ordinare questo in un nuovo array, se necessario.


3

L'ho appena scoperto nel modo più duro.

Usando React con Redux, il contenitore di stato di cui voglio attraversare le chiavi per generare figli viene aggiornato ogni volta che il negozio viene cambiato (secondo i concetti di immutabilità di Redux).

Quindi, per prendere Object.keys(valueFromStore)ho usato Object.keys(valueFromStore).sort(), in modo che almeno ora ho un ordine alfabetico per le chiavi.


-7

Dallo standard JSON :

Un oggetto è una raccolta non ordinata di zero o più coppie nome / valore, in cui un nome è una stringa e un valore è una stringa, numero, valore booleano, null, oggetto o matrice.

(enfasi la mia).

Quindi, no, non puoi garantire l'ordine.


7
questo è specificato dallo standard ECMAScript, non dalle specifiche JSON.
Alnitak,

7
@Alnitak, @Iacqui: JSON lo prende solo dalle specifiche ECMAScript. È specificato anche per JSON, ma questo non si riferisce alla domanda.
Paŭlo Ebermann,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.