Le tri des propriétés en Javascript est rompu

15

J'ai besoin de parcourir un objet JavaScript en le traitant comme un tableau avec des clés personnalisées. Je sais que cela n'est pas entièrement pris en charge, car les propriétés n'ont pas d'ordre intrinsèque, mais comme je réorganise toujours les propriétés, j'ai trouvé cette approche simple et fiable ... jusqu'à présent.

Le problème se produit lorsque les clés sont des nombres ou des chaînes pouvant être converties en nombres.

Lorsque j'exécute ce code:

var test1 = {4294966222:"A",4294966333:"A",4294966111:"A"};
var test2 = {4294968222:"A",4294968333:"A",4294968111:"A"};
        
for (var k in test1) {console.log(k);}
console.log("---");
for (var k in test2) {console.log(k);}

la sortie est:

4294966111
4294966222
4294966333
---
4294968222
4294968333
4294968111

Ce qui signifie:

  • (test1) si les touches sont inférieures à 2 ^ 32 (4 294 967 296), elles sont automatiquement réorganisées, la plus petite en premier
  • (test2) si les touches sont supérieures à 2 ^ 32, elles ne sont PAS réorganisées.

La question est: pourquoi cela se produit-il?

Étant donné que tous les navigateurs que j'ai testés (Google Chrome 79.0, Mozilla Firefox 71.0, Microsoft Edge 44.18362, Internet Explorer 11.535) sont d'accord sur cette sortie, il doit y avoir des spécifications officielles.

Mise à jour

J'ai testé beaucoup de chiffres avant de découvrir que c'était une question de seuil. J'ai trouvé étrange que la séquence 2,3,1 se comporte différemment de trois horodatages ordonnés de la même manière.

Karma
la source
ma conjecture est de savoir comment le code de hachage est calculé, mais ce n'est pas une vraie réponse à votre question.
Mario Vernari
1
Je ne pense pas que ce soit cassé dans le vrai sens du terme, ils ne garantissent pas que les valeurs seront itérées par ordre car il s'exécute arbitrairement, comme vous pouvez le vérifier developer.mozilla.org/en-US/docs/Web / JavaScript / Reference /… "Remarque: for ... in ne doit pas être utilisé pour itérer sur un tableau où l'ordre des index est important." Ils garantissent seulement l'itération sur chaque élément de la collection. Quelque chose comme forEach prend en effet en compte l'ordre en parcourant les éléments dans l'ordre croissant ecma-international.org/ecma-262/5.1/#sec-15.4.4.18
Mr.Toxy
Pour mémoire, vous pouvez voir le problème directement en vous connectant test1et test2. Je pense que le "problème" vient de la mise en cache des clés dans la mise en œuvre V8 de la spécification.
Seblor
De plus, en dessous de 2 ^ 32, le nom de votre propriété est coïncidé par ordre similaire aux références de propriété internes. Vous pouvez et ne devez pas vous fier à l'ordre des propriétés des objets car, par définition, elles ne sont pas ordonnées et peuvent contenir des propriétés inhérentes à l'objet. Convertissez / mappez toujours votre objet dans un tableau, triez le tableau, puis parcourez-le si l'ordre est important.
user3154108
1
@ Mr.Toxy C'est parce que ces propriétés 4294968333et 4294968111sont supérieures à 2 ** 32(ce qui est 4294967296). Donc, ce ne sont pas des indices de tableau, ils sont donc itérés dans l'ordre de création des propriétés plutôt que dans l'ordre numérique croissant - ce qui est exactement ce qu'ils font au violon, comme prévu. (voir ma réponse)
CertainPerformance

Réponses:

4

C'est attendu. Selon la spécification , la méthode qui itère sur les propriétés,, OrdinaryOwnPropertyKeysfait:

  1. Pour chaque propre clé de propriété P de O qui est un index de tableau , dans l'ordre d'index numérique croissant, faites

    une. Ajoutez P comme dernier élément des clés.

  2. Pour chaque propre clé de propriété P de O qui est une chaîne mais pas un index de tableau, dans l'ordre chronologique croissant de création de propriété, faites

    une. Ajoutez P comme dernier élément des clés.

L'ordre numérique croissant ne s'applique qu'aux propriétés qui sont des indices de tableau.

Alors, qu'est-ce qu'un "index de tableau"? Cherchez ::

Un index entier est une clé de propriété à valeur de chaîne qui est une chaîne numérique canonique (voir 7.1.21) et dont la valeur numérique est soit +0 soit un entier positif ≤ 2 ^ 53 - 1. Un index de tableau est un index entier dont le numérique la valeur i est comprise entre +0 ≤ i <2 ^ 32 - 1.

Ainsi, les propriétés numériques supérieures à 2 ^ 32 ne sont pas des indices de tableau et sont donc itérées dans l'ordre de création des propriétés. Toutefois, les propriétés numériques qui sont moins 2^32 sont indicies de tableau et sont itéré par ordre numérique croissant.

Ainsi, par exemple:

1: Index de tableau, sera itéré numériquement

10: Index de tableau, sera itéré numériquement

4294968111: Supérieur à 2 ** 32, sera réitéré après la fin des indices du tableau, dans l'ordre de création des propriétés

9999999999999: Supérieur à 2 ** 32, sera réitéré après la fin des indices du tableau, dans l'ordre de création des propriétés

De plus, gardez à l'esprit que, contrairement à la croyance populaire, l'ordre d'itération des propriétés est également garanti par la spécification, grâce à la proposition d'itération for-in qui est l'étape 4.

CertainPerformance
la source
2

Cela a à voir avec la façon dont les clés d'un objet sont traversées.

Selon les spécifications ES6, il devrait être:

9.1.12 [[OwnPropertyKeys]] ( )

When the [[OwnPropertyKeys]] internal method of O is called the following steps are taken:

    Let keys be a new empty List.
    For each own property key P of O that is an integer index, in ascending numeric index order
        Add P as the last element of keys.
    For each own property key P of O that is a String but is not an integer index, in property creation order
        Add P as the last element of keys.
    For each own property key P of O that is a Symbol, in property creation order
        Add P as the last element of keys.
    Return keys.

http://www.ecma-international.org/ecma-262/6.0/#sec-ordinary-object-internal-methods-and-internal-slots-ownpropertykeys

Cela signifie que si la valeur d'une clé reste la même si elle est convertie en un nombre non signé de 53 bits et en arrière, elle est traitée comme un index entier qui est trié par ordre numérique croissant.

Si cela échoue, il est traité comme une clé de chaîne, qui est ordonnée de la façon dont elle a été ajoutée à l'objet.

Le hic ici est que tous les principaux navigateurs ne suivent pas encore cette spécification et utilisent à la place un index de tableau qui est limité à un nombre positif jusqu'à 2 ^ 32-1. Donc, tout ce qui dépasse cette limite est en fait une clé de chaîne.

obscur
la source