Ordine degli elementi in un ciclo “for (… in …)”

Il ciclo “for … in” in Javascript cicla attraverso gli hashtables / elementi nell’ordine in cui sono dichiarati? Esiste un browser che non lo fa in ordine?
L’object che desidero utilizzare sarà dichiarato una volta e non sarà mai modificato.

Supponiamo di avere:

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

E li uso ulteriormente in:

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

Posso aspettarmi che “A:” Hello “” venga sempre prima di “B:” World “” in molti browser decenti?

Citando John Resig :

Attualmente tutti i principali browser eseguono il loop sulle proprietà di un object nell’ordine in cui sono stati definiti. Anche Chrome fa questo, tranne che per un paio di casi. […] Questo comportamento è esplicitamente lasciato indefinito dalla specifica ECMAScript. In ECMA-262, sezione 12.6.4:

La meccanica di enumerazione delle proprietà … dipende dall’implementazione.

Tuttavia, le specifiche sono abbastanza diverse dall’implementazione. Tutte le moderne implementazioni di ECMAScript eseguono l’iterazione attraverso le proprietà dell’object nell’ordine in cui sono state definite. Per questo motivo, il team di Chrome ha ritenuto che fosse un bug e lo risolverà.

Tutti i browser rispettano l’ordine di definizione con l’eccezione di Chrome e Opera che fanno per ogni nome di proprietà non numerico. In questi due browser le proprietà vengono ordinate in anticipo rispetto alla prima proprietà non numerica (questo ha a che fare con il modo in cui implementano gli array). L’ordine è lo stesso anche per Object.keys .

Questo esempio dovrebbe chiarire cosa succede:

 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" 

I dettagli tecnici di questo sono meno importanti del fatto che questo potrebbe cambiare in qualsiasi momento. Non fare affidamento su cose che rimangono in questo modo.

In breve: usa un array se l’ordine è importante per te.

Bumping questo un anno dopo …

È il 2012 e i principali browser sono ancora diversi:

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

sintesi o test nel browser corrente

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] 

Dalla specifica del linguaggio ECMAScript , sezione 12.6.4 (sul ciclo for .. in ):

La meccanica di enumerazione delle proprietà dipende dall’implementazione. L’ordine di enumerazione è definito dall’object.

E sezione 4.3.3 (definizione di “Oggetto”):

È una raccolta non ordinata di proprietà ognuna delle quali contiene un valore, un object o una funzione primitivi. Una funzione memorizzata in una proprietà di un object è chiamata metodo.

Immagino che questo significhi che non puoi fare affidamento sulle proprietà che vengono enumerate in un ordine coerente tra le implementazioni di JavaScript. (Sarebbe comunque un cattivo stile affidarsi ai dettagli specifici dell’implementazione di una lingua).

Se vuoi che il tuo ordine sia definito, dovrai implementare qualcosa che lo definisca, come un array di chiavi che hai ordinato prima di accedere all’object con esso.

Gli elementi di un object che en / en enumera sono le proprietà che non hanno il flag DontEnum. Lo standard ECMAScript, aka Javascript, dice esplicitamente che “Un object è una collezione di proprietà non ordinata” (vedere http://www.mozilla.org/js/language/E262-3.pdf sezione 8.6).

Non sarà conforms agli standard (cioè sicuro) assumere che tutte le implementazioni di Javascript vengano enumerate in ordine di dichiarazione.

Anche l’ordine di iterazione è confuso rispetto all’eliminazione delle proprietà, ma in questo 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 } 

Il desiderio di cambiare le specifiche per fissare l’ordine di ripetizione sembra essere un desiderio piuttosto popolare tra gli sviluppatori se la discussione su http://code.google.com/p/v8/issues/detail?id=164 è una indicazione.

in IE6, l’ordine non è garantito.

L’ordine non può essere considerato attendibile. Sia Opera che Chrome restituiscono l’elenco delle proprietà non ordinato.

  

Il codice sopra mostra B, A, C in Opera e C, A, B in Chrome.

Questo non risponde alla domanda in sé, ma offre una soluzione al problema di base.

Supponendo che non si possa fare affidamento sull’ordine di conservazione, perché non utilizzare una serie di oggetti con chiave e valore come proprietà?

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

Ora, sta a te assicurarti che le chiavi siano univoche (supponendo che questo sia importante anche per te. Inoltre, le modifiche dirette per l’indirizzamento e per (… in …) ora restituiscono gli indici come ‘chiavi’.

 > console.log(myArray[0].key); key1 > for (let index in myArray) {console.log(myArray[index].value);} 0 1 

Vedi la penna per (… in …) indirizzamento in ordine di JDQ ( @JDQ ) su CodePen .