¿JavaScript garantiza el orden de las propiedades de los objetos?
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é?
El orden de iteración de 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 de las claves de cadena y el orden ascendente de las claves numéricas:
// key order: 1, foo, bar
const obj = { "foo": "foo", "1": "1", "bar": "bar" }
Usar una matriz o un Map
objeto puede ser una mejor manera de lograrlo. Map
comparte algunas similitudes Object
y garantiza que las claves se iterarán en orden de inserción , sin excepción:
Las claves en el mapa están ordenadas, mientras que las claves agregadas al objeto no lo están. Por lo tanto, al iterar sobre él, un objeto Map devuelve claves en orden de inserción. (Tenga en cuenta que en la especificación ECMAScript 2015 los objetos conservan el orden de creación de cadenas y claves de símbolo, por lo que el recorrido de un objeto con, es decir, solo claves de cadena, produciría claves en orden de inserción)
Como nota, el orden de las propiedades en los objetos no estaba garantizado en absoluto 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, objeto o función primitivo. Una función almacenada en una propiedad de un objeto se llama método.
SÍ (pero no siempre por orden de inserción).
La mayoría de los navegadores iteran las propiedades de los objetos como:
- Claves enteras positivas en orden ascendente (y cadenas como "1" que se analizan como enteros)
- Claves de cadena, en orden de inserción (ES2015 garantiza esto y todos los navegadores lo cumplen)
- Nombres de símbolos, en orden de inserción (ES2015 garantiza esto y todos los navegadores lo cumplen)
Algunos navegadores más antiguos combinan las categorías 1 y 2, iterando todas las claves en orden de inserción. Si sus claves pueden analizarse como números 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 números enteros positivos (por ejemplo, "7" o "99"), donde el comportamiento varía entre navegadores. Por ejemplo, Chrome/V8 no respeta el orden de inserción cuando las claves se analizan como numéricas.
Especificaciones de lenguaje antiguo (antes de ES2015) : el orden de iteración técnicamente no estaba definido, pero todos los navegadores principales cumplían con el comportamiento de ES2015.
Tenga en cuenta que el comportamiento de ES2015 fue un buen ejemplo de cómo la especificación del lenguaje está determinada 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 del 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í surgió XHR, y Google hace lo mismo al implementar Gears y luego adoptar la funcionalidad HTML5 equivalente. La solución correcta es que ECMA incorpore formalmente el comportamiento estándar de facto en la siguiente revisión de la especificación.
El orden de las propiedades en objetos normales es un tema complejo en JavaScript.
Mientras que en ES5 no se ha especificado explícitamente ningún orden, ES2015 definió un orden en ciertos casos, y los cambios sucesivos en la especificación desde entonces han definido cada vez más el orden (incluso, a partir de ES2020, el for-in
orden del bucle). Se da el siguiente objeto:
const 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 da como resultado el siguiente orden (en ciertos casos):
Object {
0: 0,
1: "1",
2: "2",
b: "b",
a: "a",
m: function() {},
Symbol(): "sym"
}
El orden de las propiedades "propias" (no heredadas) es:
- Claves positivas tipo entero en orden ascendente
- Claves de cadena en orden de inserción
- Símbolos en orden de inserción
Así, hay tres segmentos, lo que puede alterar el orden de inserción (como sucedió en el ejemplo). Y las claves positivas similares a números enteros no se ciñen al orden de inserción en absoluto.
En ES2015, solo ciertos métodos seguían el orden:
- Asignar objeto
- Objeto.defineProperties
- Objeto.getOwnPropertyNames
- Objeto.getOwnPropertySymbols
- Reflect.ownKeys
- JSON.parse
- JSON.stringify
A partir de ES2020, todos los demás lo hacen (algunos con especificaciones entre ES2015 y ES2020, otros en ES2020), que incluyen:
- Claves.de.objeto, Entradas.de.objeto, Valores.de.objeto, ...
- para..en
Lo más difícil de concretar fue for-in
que, singularmente, incluye propiedades heredadas. Eso se hizo (en todos los casos excepto en los extremos) en ES2020. La siguiente lista de la propuesta vinculada (ahora completa) proporciona los casos extremos en los que no se especifica el orden:
- Ni el objeto que se itera ni nada en su cadena de prototipo es un proxy, una matriz escrita, un objeto de espacio de nombres de módulo ni un objeto exótico de host.
- Ni el objeto ni nada en su cadena de prototipo cambia de prototipo durante la iteración.
- Ni el objeto ni nada en su cadena de prototipo tiene una propiedad eliminada durante la iteración.
- Nada en la cadena de prototipos del objeto tiene una propiedad agregada durante la iteración.
- Ninguna propiedad del objeto ni nada en su cadena de prototipo cambia su enumerabilidad durante la iteración.
- Ninguna propiedad no enumerable ensombrece a una enumerable.
Conclusión: incluso en ES2015 no deberías confiar en el orden de las propiedades de los objetos normales en JavaScript. Es propenso a errores. Si necesita pares con nombres ordenados, utilice Map
en su lugar, que utiliza únicamente el orden de inserción. Si solo necesita orden, use una matriz o Set
(que también usa puramente orden de inserción).
Al momento de escribir este artículo, la mayoría de los navegadores devolvían propiedades en el mismo orden en que se insertaron, pero no se garantizaba explícitamente el comportamiento, por lo que no se debería haber confiado en él.
La especificación ECMAScript solía decir:
La mecánica y el orden de enumeración de las propiedades... no están especificados.
Sin embargo, en ES2015 y posteriores, las claves que no sean enteras se devolverán en orden de inserción.