¿JavaScript garantiza el orden de propiedad del objeto?

647

Si creo un objeto como este:

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

¿El objeto resultante siempre se verá así?

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

Es decir, ¿las propiedades estarán en el mismo orden en que las agregué?

suave
fuente
1
posible duplicado del orden
Borgar
1
@TJCrowder ¿Podrías entrar en más detalles sobre por qué la respuesta aceptada ya no es precisa? La pregunta que vinculó parece reducirse a la idea de que el orden de propiedad aún no está garantizado por especificación.
zero298
2
@ zero298: La respuesta aceptada a esa pregunta describe claramente el orden de propiedad especificado a partir de ES2015 +. Operaciones de legado ( for-in, Object.keys) no tienen que apoyarlo (oficialmente), pero no es para ahora. (Extraoficialmente: Firefox, Chrome y Edge siguen el orden especificado incluso en for-in y Object.keys, donde no están oficialmente obligados a: jsfiddle.net/arhbn3k2/1 )
TJ Crowder

Respuestas:

474

El orden de iteración para los objetos sigue un cierto conjunto de reglas desde ES2015, pero no (siempre) sigue el orden de inserción . En pocas palabras, el orden de iteración es una combinación del orden de inserción para las teclas de cadenas y el orden ascendente para las teclas numéricas:

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

Usar una matriz o un Mapobjeto puede ser una mejor manera de lograr esto. Mapcomparte algunas similitudes Objecty garantiza que las claves se repitan en orden de inserción , sin excepción:

Las claves en el Mapa se ordenan mientras que las claves agregadas al objeto no. Por lo tanto, al iterar sobre él, un objeto Map devuelve claves en orden de inserción. (Tenga en cuenta que en los objetos de especificación ECMAScript 2015 se conserva el orden de creación de las cadenas y las teclas de símbolos, por lo que el desplazamiento de un objeto con, por ejemplo, solo las teclas de cadena produciría las claves en el orden de inserción)

Como nota, el orden de las propiedades en los objetos no estaba garantizado antes de ES2015. Definición de un objeto de ECMAScript Tercera edición (pdf) :

4.3.3 Objeto

Un objeto es un miembro del tipo Objeto. Es una colección desordenada de propiedades, cada una de las cuales contiene un valor primitivo, objeto o función. Una función almacenada en una propiedad de un objeto se llama método.

bpierre
fuente
El comportamiento de las teclas de enteros no es consistente en todos los navegadores. Algunos navegadores antiguos iteran las teclas enteras en orden de inserción (con las teclas de cadena) y algunas en orden ascendente.
Dave Dopson
1
@DaveDopson - Derecha: los navegadores obsoletos no siguen la especificación actual porque no están actualizados.
TJ Crowder
199

SÍ (para claves no enteras).

La mayoría de los navegadores iteran las propiedades del objeto como:

  1. Teclas enteras en orden ascendente (y cadenas como "1" que se analizan como ints)
  2. Teclas de cadena, en orden de inserción (ES2015 garantiza esto y todos los navegadores cumplen)
  3. Nombres de símbolos, en orden de inserción (ES2015 garantiza esto y todos los navegadores cumplen)

Algunos navegadores antiguos combinan las categorías 1 y 2, iterando todas las claves en orden de inserción. Si sus claves pueden analizarse como enteros, es mejor no confiar en ningún orden de iteración específico.

El orden de inserción de la especificación de idioma actual (desde ES2015) se conserva, excepto en el caso de claves que se analizan como enteros (por ejemplo, "7" o "99"), donde el comportamiento varía entre los navegadores. Por ejemplo, Chrome / V8 no respeta el orden de inserción cuando las claves se analizan como numéricas.

Especificación de idioma antiguo (antes de ES2015) : el orden de iteración era técnicamente indefinido, pero todos los principales navegadores cumplían con el comportamiento de ES2015.

Tenga en cuenta que el comportamiento ES2015 fue un buen ejemplo de la especificación del lenguaje impulsada por el comportamiento existente, y no al revés. Para tener una idea más profunda de esa mentalidad de compatibilidad con versiones anteriores, consulte http://code.google.com/p/v8/issues/detail?id=164 , un error de Chrome que cubre en detalle las decisiones de diseño detrás del comportamiento de orden de iteración de Chrome . Según uno de los comentarios (bastante obstinados) sobre ese informe de error:

Los estándares siempre siguen a las implementaciones, de ahí proviene XHR, y Google hace lo mismo al implementar Gears y luego adoptar una funcionalidad HTML5 equivalente. La solución correcta es hacer que ECMA incorpore formalmente el comportamiento estándar de facto en la próxima revisión de la especificación.

Dave Dopson
fuente
2
@BenjaminGruenbaum: ese era exactamente mi punto. A partir de 2014, todos los principales proveedores tenían una implementación común y, por lo tanto, los estándares eventualmente seguirán (es decir, en 2015).
Dave Dopson el
2
Por cierto: React createFragmentAPI ya se basa en esto ... mi
mik01aj
77
@BenjaminGruenbaum Tu comentario es falso. En ES2015, el pedido está garantizado solo para los métodos seleccionados . Ver la respuesta de ftor a continuación.
Piotr Dobrogost
84

El orden de las propiedades en los objetos normales es un tema complejo en Javascript.

Mientras que en ES5 explícitamente no se ha especificado ningún pedido, ES2015 tiene un pedido en ciertos casos. Dado es el siguiente objeto:

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

Esto resulta en el siguiente orden (en ciertos casos):

Object {
  0: 0,
  1: "1",
  2: "2",
  b: "b",
  a: "a",
  m: function() {},
  Symbol(): "sym"
}
  1. teclas de tipo entero en orden ascendente
  2. claves normales en orden de inserción
  3. Símbolos en orden de inserción

Por lo tanto, hay tres segmentos que pueden alterar el orden de inserción (como sucedió en el ejemplo). Y las teclas de tipo entero no se adhieren al orden de inserción en absoluto.

La pregunta es, ¿para qué métodos está garantizado este pedido en la especificación ES2015?

Los siguientes métodos garantizan el orden que se muestra:

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

Los siguientes métodos / bucles no garantizan ningún orden en absoluto:

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

Conclusión: incluso en ES2015 no debe confiar en el orden de las propiedades de los objetos normales en Javascript. Es propenso a errores. Usar en su Maplugar.

evolutionxbox
fuente
Probé aproximadamente la conclusión en el nodo v8.11 y es correcta.
merlin.ye
1
@BenjaminGruenbaum alguna idea?
evolutionxbox
Object.entries garantiza el orden, siguiendo la regla de enteros
Mojimi
1
+1 para "Incluso en ES2015, no debe confiar en el orden de las propiedades de los objetos normales en Javascript. Es propenso a errores. Utilice Map en su lugar". No puede confiar en el orden de la propiedad cuando el cumplimiento es complicado. ¿Cómo prueba esto para la comparabilidad hacia atrás si tiene que admitir navegadores antiguos?
zero298
1
¿Existe alguna documentación oficial al respecto o una referencia?
Batir el
66

En el momento de la escritura, la mayoría de los navegadores devolvieron las propiedades en el mismo orden en que se insertaron, pero explícitamente no se garantizó el comportamiento, por lo que no debería haber sido utilizado.

La especificación ECMAScript solía decir:

La mecánica y el orden de enumerar las propiedades ... no se especifica.

Sin embargo, en ES2015 y posteriores, las claves no enteras se devolverán en orden de inserción.

Alnitak
fuente
16
Chrome implementa un orden diferente al de otros navegadores. Ver code.google.com/p/v8/issues/detail?id=164
Tim Down
99
Opera 10.50 y superior, así como IE9, coinciden con el pedido de Chrome. Firefox y Safari ahora son una minoría (y ambos también usan diferentes órdenes para Objetos / Matrices).
gsnedders
1
@Veverke no existe explícitamente ninguna garantía en el pedido, por lo que siempre se debe suponer que el pedido es efectivamente aleatorio.
Alnitak
1
@Veverke No, el orden no es nada predecible. Depende de la implementación y está sujeto a cambios en cualquier momento, y podría cambiar (por ejemplo) cada vez que su navegador se actualice.
Alnitak
3
Esta respuesta es falsa en ES2015.
Benjamin Gruenbaum
42

Toda esta respuesta está en el contexto del cumplimiento de especificaciones, no lo que hace cualquier motor en un momento particular o históricamente.

En general, no

La pregunta real es muy vaga.

las propiedades estarán en el mismo orden en que las agregué

¿En que contexto?

La respuesta es: depende de varios factores. En general, no .

A veces sí

Aquí es donde puede contar con el orden de las claves de propiedad para plain Objects:

  • Motor compatible con ES2015
  • Propiedades propias
  • Object.getOwnPropertyNames()` Reflect.ownKeys()`Object.getOwnPropertySymbols(O)

En todos los casos, estos métodos incluyen claves de propiedad no enumerables y claves de orden como se especifica en [[OwnPropertyKeys]](ver más abajo). Difieren en el tipo de valores clave que incluyen ( Stringy / o Symbol). En este contexto Stringincluye valores enteros.

Object.getOwnPropertyNames(O)

Devuelve Olas Stringpropiedades con clave propia ( nombres de propiedad ).

Reflect.ownKeys(O)

Devuelve Olas propiedades propias Stringy con Symbolclave.

Object.getOwnPropertySymbols(O)

Devuelve Olas Symbolpropiedades de clave propia .

[[OwnPropertyKeys]]

El orden es esencialmente: tipo entero Stringsen orden ascendente, no tipo entero Stringsen orden de creación, símbolos en orden de creación. Dependiendo de qué función invoque esto, algunos de estos tipos pueden no estar incluidos.

El lenguaje específico es que las claves se devuelven en el siguiente orden:

  1. ... cada clave Pde propiedad propia O[del objeto que se está iterando] que es un índice entero, en orden de índice numérico ascendente

  2. ... cada clave Pde propiedad propia Oes una cadena pero no un índice entero, en orden de creación de propiedades

  3. ... cada clave Pde propiedad propia de Oeso es un símbolo, en orden de creación de propiedad

Map

Si está interesado en los mapas ordenados, debería considerar usar el Maptipo introducido en ES2015 en lugar de simple Objects.

JMM
fuente
13

En los navegadores modernos, puede usar la Mapestructura de datos en lugar de un objeto.

Desarrollador mozilla> Mapa

Un objeto Map puede iterar sus elementos en orden de inserción ...

Tópico
fuente
7

En ES2015, lo hace, pero no a lo que podrías pensar

El orden de las claves en un objeto no estaba garantizado hasta ES2015. Fue definido por la implementación.

Sin embargo, en ES2015 en se especificó en. Al igual que muchas cosas en JavaScript, esto se hizo con fines de compatibilidad y, en general, reflejaba un estándar no oficial existente entre la mayoría de los motores JS (con una excepción).

El orden se define en la especificación, bajo la operación abstracta OrdinaryOwnPropertyKeys , que sustenta todos los métodos de iteración sobre las propias claves de un objeto. Parafraseado, el orden es el siguiente:

  1. Todos índice entero teclas (cosas por el estilo "1123", "55"etc) en orden numérico ascendente.

  2. Todas las claves de cadena que no son índices enteros, en orden de creación (más antiguo-primero).

  3. Todas las teclas de símbolos, en orden de creación (más antiguo-primero).

Es tonto decir que el pedido no es confiable: es confiable, probablemente no sea lo que desea, y los navegadores modernos implementan este pedido correctamente.

Algunas excepciones incluyen métodos para enumerar claves heredadas, como el for .. inbucle. El for .. inbucle no garantiza el orden de acuerdo con las especificaciones.

GregRos
fuente
7

A partir de ES2015, el orden de las propiedades está garantizado para ciertos métodos que iteran sobre las propiedades. Pero no otros . Desafortunadamente, los métodos que no se garantiza que tengan un pedido son generalmente los más utilizados:

  • Object.keys` Object.values`Object.entries
  • for..in bucles
  • JSON.stringify

Pero, a partir de ES2020, el orden de propiedad de estos métodos que anteriormente no eran confiables estará garantizado por la especificación para que se repita de la misma manera determinista que los demás, debido a la propuesta terminada : mecánica for-in .

Al igual que con los métodos que tienen un orden de iteración garantizado (like Reflect.ownKeysy Object.getOwnPropertyNames), los métodos no especificados previamente también iterarán en el siguiente orden:

  • Teclas de matriz numérica, en orden numérico ascendente
  • Todas las demás teclas que no sean símbolos, en orden de inserción
  • Teclas de símbolos, en orden de inserción

Esto es lo que casi todas las implementaciones ya hacen (y lo han hecho durante muchos años), pero la nueva propuesta lo ha hecho oficial.

Aunque la especificación actual deja ... en orden de iteración " casi totalmente sin especificar , los motores reales tienden a ser más consistentes:"

La falta de especificidad en ECMA-262 no refleja la realidad. En una discusión que se remonta a años atrás, los implementadores han observado que existen algunas restricciones en el comportamiento de for-in que cualquier persona que quiera ejecutar código en la web debe seguir.

Debido a que cada implementación ya itera sobre las propiedades de manera predecible, se puede incluir en la especificación sin romper la compatibilidad con versiones anteriores.


Hay algunos casos extraños en los que las implementaciones actualmente no están de acuerdo, y en tales casos, el orden resultante continuará sin especificarse. Para garantizar el orden de la propiedad :

Ni el objeto que se está iterando ni nada en su cadena de prototipo es un proxy, una matriz con tipo, un objeto de espacio de nombres de módulo o un objeto exótico de host.

Ni el objeto ni nada en su cadena de prototipo tiene su cambio de prototipo durante la iteración.

Ni el objeto ni nada en su cadena de prototipos tiene una propiedad eliminada durante la iteración.

Nada en la cadena prototipo del objeto tiene una propiedad agregada durante la iteración.

Ninguna propiedad del objeto ni nada en su cadena de prototipos tiene su cambio de enumeración durante la iteración.

Ninguna propiedad no enumerable sombrea una enumerable.

Cierto rendimiento
fuente
5

Como han dicho otros, no tiene garantía en cuanto al orden cuando itera sobre las propiedades de un objeto. Si necesita una lista ordenada de múltiples campos, le sugerí crear una matriz de objetos.

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

De esta manera, puede usar un bucle for regular y tener el orden de inserción. A continuación, puede utilizar el método de ordenación de matriz para ordenar esto en una nueva matriz si es necesario.

Drew Durham
fuente
3

Acabo de descubrir esto de la manera difícil.

Usando React with Redux, el contenedor de estado de las claves de las cuales quiero atravesar para generar hijos se actualiza cada vez que se cambia la tienda (según los conceptos de inmutabilidad de Redux).

Por lo tanto, para tomar Object.keys(valueFromStore)solía Object.keys(valueFromStore).sort(), de modo que al menos ahora tengo un orden alfabético para las teclas.

kriskate
fuente
-7

Del estándar JSON :

Un objeto es una colección desordenada de cero o más pares de nombre / valor, donde un nombre es una cadena y un valor es una cadena, número, booleano, nulo, objeto o matriz.

(énfasis mío).

Entonces, no, no puede garantizar el pedido.

Kevin Lacquement
fuente
77
esto lo especifica el estándar ECMAScript, no la especificación JSON.
Alnitak
77
@Alnitak, @Iacqui: JSON solo toma esto de la especificación ECMAScript. También se especifica para JSON, pero esto realmente no se relaciona con la pregunta.
Paŭlo Ebermann