Ordenar propiedades en Javascript está roto

15

Necesito recorrer un objeto JavaScript tratándolo como una matriz con claves personalizadas. Sé que esto no es totalmente compatible, ya que las propiedades no tienen un orden intrínseco, pero dado que siempre vuelvo a ordenar las propiedades, encontré este enfoque simple y confiable ... hasta ahora.

El problema ocurre cuando las teclas son números o cadenas que se pueden convertir como números.

Cuando ejecuto este código:

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 salida es:

4294966111
4294966222
4294966333
---
4294968222
4294968333
4294968111

Lo que significa:

  • (prueba1) si las teclas están por debajo de 2 ^ 32 (4,294,967,296), se reordenan automáticamente, la más pequeña primero
  • (test2) si las claves están por encima de 2 ^ 32, NO se reordenan.

La pregunta es: ¿por qué está sucediendo esto?

Dado que todos los navegadores que probé (Google Chrome 79.0, Mozilla Firefox 71.0, Microsoft Edge 44.18362, Internet Explorer 11.535) están de acuerdo con esta salida, debe haber alguna especificación oficial.

Actualizar

Probé muchos números antes de descubrir que era una cuestión de umbral. Encontré extraño que la secuencia 2,3,1 se comporte de manera diferente a tres marcas de tiempo ordenadas de la misma manera.

Karma
fuente
Supongo que es cómo se calcula el código hash, pero no es una respuesta real a su pregunta.
Mario Vernari
1
No creo que esté roto en el sentido coloquial real de la palabra, no garantizan que los valores se iterarán por orden, ya que se ejecuta arbitrariamente, ya que puede consultar developer.mozilla.org/en-US/docs/Web / JavaScript / Reference / ... "Nota: for ... in no debe usarse para iterar sobre una matriz donde el orden del índice es importante". Solo garantizan la iteración sobre cada elemento de la colección. Algo así para cada uno tiene en cuenta el orden al atravesar los elementos en orden ascendente ecma-international.org/ecma-262/5.1/#sec-15.4.4.18
Mr.Toxy
Para el registro, puede ver el problema directamente iniciando sesión test1y test2. Creo que el "problema" proviene del almacenamiento en caché de claves en la implementación V8 de la especificación.
Seblor
Es más que debajo de 2 ^ 32 su nombre de propiedad se ordena de manera coincidente similar a las referencias internas de propiedad. Puede y no debe confiar en el orden de las propiedades de los objetos, ya que, por definición, no están ordenados y pueden contener propiedades inherentes a los objetos. Siempre convierta / asigne su objeto en una matriz, ordene la matriz y luego repítela si el orden es importante.
user3154108
1
@ Mr.Toxy Eso es porque esas propiedades 4294968333y 4294968111son mayores que 2 ** 32(que es 4294967296). Por lo tanto, no son indicadores de matriz, por lo que se repiten en orden de creación de propiedad, en lugar de en orden numérico ascendente, que es exactamente lo que están haciendo en el violín, como se esperaba. (ver mi respuesta)
CertainPerformance

Respuestas:

4

Esto es de esperarse. Según la especificación , el método que itera sobre las propiedades OrdinaryOwnPropertyKeys, hace:

  1. Para cada clave de propiedad propia P de O que sea un índice de matriz , en orden de índice numérico ascendente, haga

    a. Agregue P como último elemento de las claves.

  2. Para cada clave de propiedad propia P de O que sea una Cadena pero no un índice de matriz, en orden cronológico ascendente de creación de propiedad, haga

    a. Agregue P como último elemento de las claves.

El orden numérico ascendente solo se aplica a las propiedades que son indicadores de matriz.

Entonces, ¿qué es un "índice de matriz"? Búscalo :

Un índice entero es una clave de propiedad con valor de cadena que es una cadena numérica canónica (véase 7.1.21) y cuyo valor numérico es +0 o un entero positivo ≤ 2 ^ 53 - 1. Un índice de matriz es un índice entero cuyo número el valor i está en el rango +0 ≤ i <2 ^ 32-1.

Por lo tanto, las propiedades numéricas que son mayores que 2 ^ 32 no son indicadores de matriz y, por lo tanto, se repiten en orden de creación de propiedades. Sin embargo, las propiedades numéricas que son menores que 2^32 las de una matriz y se repiten en orden numérico ascendente.

Así por ejemplo:

1: Índice de matriz, se repetirá numéricamente

10: Índice de matriz, se repetirá numéricamente

4294968111: Mayor que 2 ** 32, se repetirá una vez que finalicen las indicaciones de la matriz, en orden de creación de propiedades

9999999999999: Mayor que 2 ** 32, se repetirá una vez que finalicen las indicaciones de la matriz, en orden de creación de propiedades

Además, tenga en cuenta que, contrariamente a la creencia popular, el orden de iteración de la propiedad también está garantizado por la especificación, gracias a la propuesta de iteración for-in que es la etapa 4.

Cierto rendimiento
fuente
2

Esto tiene que ver con la forma en que se atraviesan las teclas de un objeto.

De acuerdo con las especificaciones ES6, debería ser:

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

Eso significa que si el valor de una clave permanece igual si se convierte en un número de 53 bits sin signo y se trata como un índice entero que se ordena en orden numérico ascendente.

Si esto falla, se trata como una clave de cadena, que se ordena de la forma en que se agregaron al objeto.

El problema aquí es que todos los principales navegadores aún no siguen esta especificación y en su lugar utilizan un índice de matriz que se limita a un número positivo hasta 2 ^ 32-1. Entonces, cualquier cosa por encima de ese límite es una clave de cadena en realidad.

oscuro
fuente