Elementos ordenados en un bucle "for (... in ...)"

205

¿El bucle "for ... in" en Javascript recorre las tablas / elementos hash en el orden en que se declaran? ¿Hay un navegador que no lo hace en orden?
El objeto que deseo usar se declarará una vez y nunca se modificará.

Supongamos que tengo:

var myObject = { A: "Hello", B: "World" };

Y los uso más en:

for (var item in myObject) alert(item + " : " + myObject[item]);

¿Puedo esperar que 'A: "Hola" siempre aparezca antes de "B:" Mundo "en la mayoría de los navegadores decentes?

chakrit
fuente
61
Porque solo estarían probando un subconjunto de navegadores y variantes potenciales. Sin mencionar futuros navegadores. Es completamente erróneo suponer que una prueba que no falla proporciona cualquier tipo de prueba concreta.
James Hughes
66
Dudo que mi capacidad limitada de JavaScript sea mejor que la multitud SO. Además, ¿quién sabe qué extraño navegador acecha por ahí? Y puede ver en la respuesta que GChrome tiene un error que no será evidente en mi caso de ejemplo simple.
chakrit

Respuestas:

214

Citando a John Resig :

Actualmente, todos los principales navegadores recorren las propiedades de un objeto en el orden en que se definieron. Chrome también hace esto, excepto por un par de casos. [...] Este comportamiento queda explícitamente indefinido por la especificación ECMAScript. En ECMA-262, sección 12.6.4:

La mecánica de enumerar las propiedades ... depende de la implementación.

Sin embargo, la especificación es bastante diferente de la implementación. Todas las implementaciones modernas de ECMAScript iteran a través de las propiedades del objeto en el orden en que fueron definidas. Debido a esto, el equipo de Chrome ha considerado que esto es un error y lo corregirá.

Todos los navegadores respetan el orden de definición con la excepción de Chrome y Opera, que lo hacen para cada nombre de propiedad no numérico. En estos dos navegadores, las propiedades se extraen en orden antes de la primera propiedad no numérica (esto tiene que ver con la forma en que implementan las matrices). El orden también es el mismo Object.keys.

Este ejemplo debería dejar en claro lo que sucede:

var obj = {
  "first":"first",
  "2":"2",
  "34":"34",
  "1":"1",
  "second":"second"
};
for (var i in obj) { console.log(i); };
// Order listed:
// "1"
// "2"
// "34"
// "first"
// "second"

Los tecnicismos de esto son menos importantes que el hecho de que esto puede cambiar en cualquier momento. No confíe en que las cosas se mantengan de esta manera.

En resumen: use una matriz si el orden es importante para usted.

Borgar
fuente
3
Realmente no. Chrome no implementa el mismo orden que otros navegadores: code.google.com/p/v8/issues/detail?id=164
Tim Down
20
"Use una matriz si el orden es importante para usted": ¿Qué sucede cuando usa JSON?
HM2K
11
@ HM2K, lo mismo, la especificación dice "Un objeto es una colección desordenada de cero o más pares de nombre / valor". JSON no es JavaScript: un servidor no necesita (y probablemente no lo hará) respetar el orden en que lo entrega.
Borgar
77
Firefox ya que su versión 21 ya no parece respetar el orden de inserción.
Doc Davluz
3
Esta respuesta es falsa en ES2015.
Benjamin Gruenbaum
54

Topando esto un año después ...

Es 2012 y los principales navegadores aún difieren:

function lineate(obj){
    var arr = [], i;
    for (i in obj) arr.push([i,obj[i]].join(':'));
    console.log(arr);
}
var obj = { a:1, b:2, c:3, "123":'xyz' };
/* log1 */  lineate(obj);
obj.a = 4;
/* log2 */  lineate(obj);
delete obj.a;
obj.a = 4;
/* log3 */  lineate(obj);

esencia o prueba en el navegador actual

Safari 5, Firefox 14

["a:1", "b:2", "c:3", "123:xyz"]
["a:4", "b:2", "c:3", "123:xyz"]
["b:2", "c:3", "123:xyz", "a:4"]

Chrome 21, Opera 12, Nodo 0.6, Firefox 27

["123:xyz", "a:1", "b:2", "c:3"]
["123:xyz", "a:4", "b:2", "c:3"]
["123:xyz", "b:2", "c:3", "a:4"]

IE9

[123:xyz,a:1,b:2,c:3] 
[123:xyz,a:4,b:2,c:3] 
[123:xyz,a:4,b:2,c:3] 
dvdrtrgn
fuente
16
¿Cuál es el problema aquí? Exactamente como dice la especificación, es una lista desordenada de pares clave / val.
nickf
55
nickf: las implementaciones son correctas porque hacen lo que dice la especificación. Creo que todos están de acuerdo en que la especificación de JavaScript es ... bueno, no quiero usar la palabra "incorrecto", ¿qué tal "extremadamente estúpido y molesto"? : P
caja
10
@boxed: Piensa en los objetos como un mapa hash (tabla / lo que sea). En la mayoría de los lenguajes (Java, Python, etc.) este tipo de estructuras de datos no están ordenadas. Por lo tanto, no es sorprendente que este sea el mismo caso también en JavaScript y ciertamente no hace que la especificación sea incorrecta o estúpida.
Felix Kling
9
Sinceramente, no veo el punto de esta respuesta (lo siento). El orden en el que se repiten las propiedades es un detalle de implementación, y los navegadores usan diferentes motores JavaScript, por lo que se espera que el orden difiera. Esto no cambiará.
Felix Kling
2
En una implementación de vanguardia, recibirá propiedades enteras en orden de valor numérico debido a que está respaldado por una matriz real, y las propiedades con nombre se recibirán en orden de inserción debido a la formulación de clases / formas ocultas. Lo que puede variar es si enumeran primero las propiedades enteras o primero las propiedades con nombre. La adición de deletees interesante porque al menos en V8, instantáneamente hace que el objeto sea respaldado por una tabla hash. Sin embargo, la tabla hash en V8 se almacena en orden de inserción. El resultado más interesante aquí es IE, me pregunto qué tipo de fealdad hacen para lograrlo ...
Esailija
28

De la especificación del lenguaje ECMAScript , sección 12.6.4 (en el for .. inbucle):

La mecánica de enumerar las propiedades depende de la implementación. El orden de enumeración está definido por el objeto.

Y la sección 4.3.3 (definición de "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.

Supongo que eso significa que no puede confiar en las propiedades que se enumeran en un orden consistente en todas las implementaciones de JavaScript. (De todos modos, sería un mal estilo confiar en detalles específicos de la implementación de un idioma).

Si desea definir su orden, necesitará implementar algo que lo defina, como una matriz de claves que clasifique antes de acceder al objeto con él.

Tomalak
fuente
10

Los elementos de un objeto que para / in enumera son las propiedades que no tienen establecido el indicador DontEnum. El estándar ECMAScript, también conocido como Javascript, dice explícitamente que "Un objeto es una colección desordenada de propiedades" (ver http://www.mozilla.org/js/language/E262-3.pdf sección 8.6).

No va a ser compatible con los estándares (es decir, seguro) asumir que todas las implementaciones de Javascript se enumerarán en orden de declaración.

Adam Wright
fuente
2
por eso está haciendo la pregunta, en lugar de simplemente suponer: p
Jamie Pate
5

El orden de iteración también se confunde con respecto a la eliminación de propiedades, pero en este caso solo con IE.

var obj = {};
obj.a = 'a';
obj.b = 'b';
obj.c = 'c';

// IE allows the value to be deleted...
delete obj.b;

// ...but remembers the old position if it is added back later
obj.b = 'bb';
for (var p in obj) {
    alert(obj[p]); // in IE, will be a, bb, then c;
                   // not a, c, then bb as for FF/Chrome/Opera/Safari
}

El deseo de cambiar la especificación para corregir el orden de iteración parece ser un deseo bastante popular entre los desarrolladores si la discusión en http://code.google.com/p/v8/issues/detail?id=164 es una indicación.

Brett Zamir
fuente
3

en IE6, el pedido no está garantizado.


fuente
2

No se puede confiar en el pedido. Tanto Opera como Chrome devuelven la lista de propiedades desordenadas.

<script type="text/javascript">
var username = {"14719":"A","648":"B","15185":"C"};

for (var i in username) {
  window.alert(i + ' => ' + username[i]);
}
</script>

El código anterior muestra B, A, C en Opera y C, A, B en Chrome.

Kouber Saparev
fuente
1

Esto no responde la pregunta per se, pero ofrece una solución al problema básico.

Suponiendo que no puede confiar en el orden para preservar, ¿por qué no utilizar una matriz de objetos con clave y valor como propiedades?

var myArray = [
    {
        'key'   : 'key1'
        'value' : 0
    },
    {
        'key'   : 'key2',
        'value' : 1
    } // ...
];

Ahora, depende de usted asegurarse de que las claves sean únicas (suponiendo que esto también sea importante para usted. Además, los cambios de direccionamiento directo y para (... en ...) ahora devuelve índices como 'claves'.

> console.log(myArray[0].key);
key1

> for (let index in myArray) {console.log(myArray[index].value);}
0
1
Vea la Pluma para (... en ...) direccionamiento en orden por JDQ ( @JDQ ) en CodePen .

JDQ
fuente