Ordre des éléments dans une boucle «pour (… dans…)»

205

La boucle "for… in" en Javascript parcourt-elle les tables de hachage / éléments dans l'ordre dans lequel elles sont déclarées? Y a-t-il un navigateur qui ne le fait pas dans l'ordre?
L'objet que je souhaite utiliser sera déclaré une fois et ne sera jamais modifié.

Supposons que j'ai:

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

Et je les utilise en outre dans:

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

Puis-je m'attendre à ce que 'A: "Bonjour"' apparaisse toujours avant 'B: "Monde"' dans la plupart des navigateurs décents?

chakrit
la source
61
Étant donné qu'ils ne testeraient qu'un sous-ensemble de navigateurs et de variantes potentiels. Sans parler des futurs navigateurs. Il est tout à fait faux de supposer qu'un test non défaillant fournit une sorte de preuve concrète.
James Hughes
6
Je doute que ma propre capacité javascript limitée soit meilleure que la foule SO. D'ailleurs, qui sait quel étrange navigateur se cache là-bas? Et vous pouvez voir dans la réponse que GChrome a un bug qui ne sera pas apparent dans mon cas d'exemple simple.
chakrit
doublon possible de Est
Bergi

Réponses:

214

Citant John Resig :

Actuellement, tous les principaux navigateurs parcourent les propriétés d'un objet dans l'ordre dans lequel elles ont été définies. Chrome le fait également, à l'exception de quelques cas. [...] Ce comportement n'est explicitement pas défini par la spécification ECMAScript. Dans ECMA-262, section 12.6.4:

La mécanique d'énumération des propriétés ... dépend de l'implémentation.

Cependant, la spécification est assez différente de l'implémentation. Toutes les implémentations modernes d'ECMAScript parcourent les propriétés des objets dans l'ordre dans lequel elles ont été définies. Pour cette raison, l'équipe Chrome a considéré qu'il s'agissait d'un bogue et le corrigera.

Tous les navigateurs respectent l'ordre de définition à l'exception de Chrome et Opera qui le font pour chaque nom de propriété non numérique. Dans ces deux navigateurs, les propriétés sont extraites dans l'ordre avant la première propriété non numérique (cela a à voir avec la façon dont elles implémentent les tableaux). L'ordre est également le même pour Object.keys.

Cet exemple devrait montrer clairement ce qui se passe:

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"

Les détails techniques sont moins importants que le fait que cela puisse changer à tout moment. Ne comptez pas sur des choses qui restent ainsi.

En bref: utilisez un tableau si l'ordre est important pour vous.

Borgar
la source
3
Pas vraiment. Chrome n'implémente
Tim Down
20
"Utilisez un tableau si l'ordre est important pour vous": qu'en est-il lorsque vous utilisez JSON?
HM2K
11
@ HM2K, Même chose, spec dit "Un objet est une collection non ordonnée de zéro ou plusieurs paires nom / valeur." JSON n'est pas JavaScript: un serveur n'a pas besoin (et ne respectera probablement pas) l'ordre que vous lui donnez.
Borgar
7
Firefox depuis sa version 21 ne semble plus respecter l'ordre d'insertion.
Doc Davluz
3
Cette réponse est fausse dans ES2015.
Benjamin Gruenbaum
54

Cogner un an plus tard ...

Nous sommes en 2012 et les principaux navigateurs diffèrent encore :

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

essentiel ou test dans le navigateur actuel

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, Node 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
la source
16
quel est le problème ici? Exactement comme le dit la spécification, il s'agit d'une liste non ordonnée de paires clé / val.
nickf
5
nickf: les implémentations ont raison en ce qu'elles font ce que dit la spécification. Je pense que tout le monde convient que la spécification javascript est .. eh bien, je ne veux pas utiliser le mot «mauvais», que diriez-vous «extrêmement stupide et ennuyeux»? : P
boxé le
10
@boxed: Considérez les objets comme une carte de hachage (table / autre). Dans la plupart des langages (Java, Python, etc.), ces types de structures de données ne sont pas triés. Il n'est donc pas surprenant que ce soit le même cas en JavaScript et cela ne rend certainement pas la spécification erronée ou stupide.
Felix Kling
9
Honnêtement, je ne vois pas l'intérêt de cette réponse (désolé). L'ordre dans lequel les propriétés sont itérées est un détail d'implémentation, et les navigateurs utilisent différents moteurs JavaScript, il est donc prévu que l'ordre diffère. Cela ne changera pas.
Felix Kling
2
Dans une implémentation à la pointe de la technologie, vous recevrez des propriétés entières dans l'ordre des valeurs numériques car elles sont soutenues par un vrai tableau, et les propriétés nommées seront reçues dans l'ordre d'insertion en raison de la formulation des classes / formes cachées. Ce qui peut varier, c'est qu'ils répertorient les propriétés entières en premier ou les propriétés nommées en premier. L'ajout de deleteest intéressant car au moins en V8, il provoque instantanément la sauvegarde de l'objet par une table de hachage. Cependant, la table de hachage dans V8 stocke dans l'ordre d'insertion. Le résultat le plus intéressant ici est IE, je me demande quel genre de laideur ils font pour y
arriver
28

D'après la spécification du langage ECMAScript , section 12.6.4 (sur lefor .. in boucle):

La mécanique d'énumération des propriétés dépend de l'implémentation. L'ordre d'énumération est défini par l'objet.

Et la section 4.3.3 (définition d '"objet"):

Il s'agit d'une collection non ordonnée de propriétés dont chacune contient une valeur primitive, un objet ou une fonction. Une fonction stockée dans une propriété d'un objet est appelée méthode.

Je suppose que cela signifie que vous ne pouvez pas compter sur les propriétés énumérées dans un ordre cohérent entre les implémentations JavaScript. (Ce serait de toute façon un mauvais style de s'appuyer sur des détails spécifiques à l'implémentation d'un langage.)

Si vous voulez que votre commande soit définie, vous devrez implémenter quelque chose qui la définit, comme un tableau de clés que vous triez avant d'accéder à l'objet avec.

Tomalak
la source
10

Les éléments d'un objet qui pour / in énumèrent sont les propriétés qui n'ont pas l'indicateur DontEnum défini. La norme ECMAScript, alias Javascript, dit explicitement qu '"un objet est une collection non ordonnée de propriétés" (voir http://www.mozilla.org/js/language/E262-3.pdf section 8.6).

Il ne sera pas conforme aux normes (c'est-à-dire sûr) de supposer que toutes les implémentations Javascript seront énumérées dans l'ordre de déclaration.

Adam Wright
la source
2
c'est pourquoi il pose la question, au lieu de simplement supposer: p
Jamie Pate
5

L'ordre d'itération est également confondu en ce qui concerne la suppression des propriétés, mais dans ce cas avec IE uniquement.

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
}

Le désir de changer la spécification pour corriger l'ordre d'itération semble être un désir assez populaire parmi les développeurs si la discussion sur http://code.google.com/p/v8/issues/detail?id=164 est une indication.

Brett Zamir
la source
3

dans IE6, la commande n'est pas garantie.


la source
2

La commande n'est pas fiable. Opera et Chrome renvoient la liste des propriétés non ordonnées.

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

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

Le code ci-dessus montre B, A, C dans Opera et C, A, B dans Chrome.

Kouber Saparev
la source
1

Cela ne répond pas à la question en soi, mais offre une solution au problème de base.

En supposant que vous ne pouvez pas compter sur l'ordre pour le conserver, pourquoi ne pas utiliser un tableau d'objets avec la clé et la valeur comme propriétés?

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

Maintenant, c'est à vous de vous assurer que les clés sont uniques (en supposant que cela est également important pour vous. De même, les changements d'adressage direct, et pour (... dans ...) renvoie maintenant les index en tant que «clés».

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

> for (let index in myArray) {console.log(myArray[index].value);}
0
1
Voir le Pen pour (... dans ...) l'adressage par JDQ ( @JDQ ) sur CodePen .

JDQ
la source