Gli elementi ordinano in un ciclo "for (... in ...)"


205

Il ciclo "for ... in" in Javascript scorre attraverso gli hashtables / elementi nell'ordine in cui sono dichiarati? Esiste un browser che non lo fa in ordine?
L'oggetto che desidero utilizzare verrà dichiarato una volta e non verrà 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: "Ciao"' preceda sempre 'B: "Mondo"' nella maggior parte dei browser decenti?


61
Perché avrebbero testato solo un sottoinsieme di potenziali browser e varianti. Per non parlare dei futuri browser. È assolutamente errato supporre che un test non fallito fornisca qualsiasi tipo di prova concreta.
James Hughes,

6
Dubito che la mia capacità javascript limitata sarà migliore della folla SO. Inoltre chi sa quale strano browser si nasconde là fuori? E puoi vedere nella risposta che GChrome ha un bug che non sarà evidente nel mio semplice esempio.
chakrit,


Risposte:


214

Citando John Resig :

Attualmente tutti i principali browser eseguono il ciclo delle proprietà di un oggetto nell'ordine in cui sono stati definiti. Anche Chrome fa questo, tranne un paio di casi. [...] Questo comportamento è esplicitamente lasciato non definito dalla specifica ECMAScript. Nell'ECMA-262, sezione 12.6.4:

La meccanica dell'enumerazione delle proprietà ... dipende dall'implementazione.

Tuttavia, le specifiche sono abbastanza diverse dall'implementazione. Tutte le implementazioni moderne di ECMAScript ripetono le proprietà degli oggetti nell'ordine in cui sono state definite. Per questo motivo il team di Chrome ha ritenuto che si trattasse di un bug e lo risolverà.

Tutti i browser rispettano l'ordine delle definizioni, ad eccezione di Chrome e Opera, che fanno per ogni nome di proprietà non numerico. In questi due browser le proprietà vengono tirate in ordine prima della prima proprietà non numerica (ciò ha a che fare con il modo in cui implementano le matrici). 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 tecnicismi di questo sono meno importanti del fatto che ciò può cambiare in qualsiasi momento. Non fare affidamento su cose che rimangono così.

In breve: utilizzare un array se l'ordine è importante per te.


3
Non proprio. Chrome non implementa lo stesso ordine degli altri browser: code.google.com/p/v8/issues/detail?id=164
Tim Down

20
"Usa un array se l'ordine è importante per te": che dire di quando stai usando JSON?
HM2K,

11
@ HM2K, stessa cosa, la specifica dice "Un oggetto è una raccolta non ordinata di zero o più coppie nome / valore". JSON non è JavaScript: un server non ha bisogno (e probabilmente non lo farà) di rispettare l'ordine in cui lo gestisci.
Borgar,

7
Firefox dalla sua versione 21 non sembra più rispettare l'ordine di inserimento.
Doc Davluz,

3
Questa risposta è falsa in ES2015.
Benjamin Gruenbaum,

54

Incontrarlo un anno dopo ...

È il 2012 e i principali browser differiscono ancora :

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

gist 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] 

16
qual è il problema qui? Esattamente come dice la specifica, è un elenco non ordinato di coppie chiave / val.
Nickf

5
nickf: le implementazioni sono giuste in quanto fanno ciò che dice la specifica. Penso che tutti siano d'accordo sul fatto che le specifiche di JavaScript siano .. beh, non voglio usare la parola "sbagliato", che ne dici di "estremamente stupido e fastidioso"? : P
scatola il

10
@boxed: pensa agli oggetti come una mappa hash (tabella / qualunque cosa). Nella maggior parte delle lingue (Java, Python, ecc.) Questi tipi di strutture di dati non sono ordinati. Quindi non sorprende che questo sia lo stesso caso anche in JavaScript e certamente non rende le specifiche errate o stupide.
Felix Kling

9
Onestamente non vedo il punto di questa risposta (scusate). L'ordine in cui vengono ripetute le proprietà è un dettaglio dell'implementazione e i browser utilizzano motori JavaScript diversi, quindi si prevede che l'ordine differisca. Questo non cambierà.
Felix Kling

2
In un'implementazione allo stato dell'arte riceverai proprietà intere in ordine numerico a causa del supporto di un array reale e le proprietà nominate verranno ricevute in ordine di inserimento a causa della formulazione nascosta di classi / forme. Ciò che può variare è se elencano prima le proprietà intere o prima le proprietà con nome. L'aggiunta di deleteè interessante perché almeno in V8, fa immediatamente eseguire il backup dell'oggetto da una tabella hash. Tuttavia, la tabella hash in V8 viene archiviata in ordine di inserimento. Il risultato più interessante qui è IE, mi chiedo che tipo di bruttezza facciano per
riuscirci

28

Da ECMAScript Language Specification , sezione 12.6.4 (sul for .. inloop):

La meccanica dell'enumerazione delle proprietà dipende dall'implementazione. L'ordine di enumerazione è definito dall'oggetto.

E sezione 4.3.3 (definizione di "Oggetto"):

È 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.

Immagino che ciò significhi che non puoi fare affidamento sulle proprietà enumerate in un ordine coerente tra le implementazioni JavaScript. (Sarebbe comunque un cattivo stile fare affidamento su dettagli specifici di una lingua per l'implementazione.)

Se vuoi che il tuo ordine sia definito, dovrai implementare qualcosa che lo definisce, come una matrice di chiavi che ordini prima di accedere all'oggetto con esso.


10

Gli elementi di un oggetto che per / in enumera sono le proprietà che non hanno il flag DontEnum impostato. Lo standard ECMAScript, noto anche come Javascript, afferma esplicitamente che "Un oggetto è una raccolta non ordinata di proprietà" (vedere http://www.mozilla.org/js/language/E262-3.pdf sezione 8.6).

Non sarà conforme agli standard (vale a dire sicuro) supporre che tutte le implementazioni di Javascript vengano elencate in ordine di dichiarazione.


2
ed è per questo che sta ponendo la domanda, invece di supporre: p
Jamie Pate,

5

Anche l'ordine di iterazione viene 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 modificare le specifiche per correggere l'ordine di iterazione sembra essere un desiderio piuttosto popolare tra gli sviluppatori se la discussione su http://code.google.com/p/v8/issues/detail?id=164 è un'indicazione.


3

in IE6, l'ordine non è garantito.


2

L'ordine non può essere attendibile. Sia Opera che Chrome restituiscono l'elenco delle proprietà non ordinate.

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

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

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


1

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

Supponendo che non si possa fare affidamento sull'ordine per preservarlo, perché non utilizzare una matrice di oggetti con chiave e valore come proprietà?

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

Ora spetta a te assicurarti che le chiavi siano univoche (supponendo che ciò sia importante anche per te. Inoltre, le modifiche di indirizzamento diretto 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
Vedere la penna per l'indirizzamento (... in ...) in ordine da JDQ ( @JDQ ) su CodePen .

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.