L'ordinamento delle proprietà in Javascript è interrotto


15

Devo passare in rassegna un oggetto JavaScript trattandolo come un array con chiavi personalizzate. So che questo non è completamente supportato, dal momento che le proprietà non hanno un ordine istruttivo, ma dal momento che riordino sempre le proprietà, ho trovato questo approccio semplice e affidabile ... fino ad ora.

Il problema si verifica quando le chiavi sono numeri o stringhe che possono essere espressi come numeri.

Quando eseguo questo codice:

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

l'output è:

4294966111
4294966222
4294966333
---
4294968222
4294968333
4294968111

Che significa:

  • (test1) se i tasti sono inferiori a 2 ^ 32 (4.294.967.296), vengono automaticamente riordinati, il più piccolo per primo
  • (test2) se i tasti sono superiori a 2 ^ 32, NON vengono riordinati.

La domanda è: perché sta succedendo questo?

Poiché tutti i browser che ho testato (Google Chrome 79.0, Mozilla Firefox 71.0, Microsoft Edge 44.18362, Internet Explorer 11.535) concordano su questo output, ci devono essere alcune specifiche ufficiali.

Aggiornare

Ho testato molti numeri prima di scoprire che era una questione di soglia. Ho trovato strano che la sequenza 2,3,1 si comporti diversamente da tre timestamp ordinati allo stesso modo.


la mia ipotesi è come viene calcolato il codice hash, ma non è una vera risposta alla tua domanda.
Mario Vernari,

1
Non penso che sia rotto nel vero senso colloquiale della parola, non garantiscono che i valori saranno iterati per ordine poiché funziona in modo arbitrario come puoi controllare developer.mozilla.org/en-US/docs/Web / JavaScript / Riferimenti / ... "Nota: per ... in non deve essere utilizzato per scorrere su una matrice in cui l'ordine dell'indice è importante." Garantiscono solo iterazioni su ogni elemento della raccolta. Qualcosa di simile forEach effettivamente prendere l'ordine in considerazione da oggetti che attraversano in ordine crescente ecma-international.org/ecma-262/5.1/#sec-15.4.4.18
Mr.Toxy

Per la cronaca, puoi vedere il problema direttamente accedendo test1e test2. Penso che il "problema" provenga dalla memorizzazione nella chiave della chiave nell'implementazione V8 delle specifiche.
Seblor

È più che al di sotto di 2 ^ 32 il nome della tua proprietà è ordinato in modo coincidente simile ai riferimenti di proprietà interni. È possibile e non fare affidamento sull'ordine delle proprietà degli oggetti poiché, per definizione, non sono ordinati e possono contenere proprietà intrinseche agli oggetti. Trasmetti sempre / mappa il tuo oggetto in un array, ordina l'array, quindi esegui il ciclo se l'ordine è importante.
user3154108

1
@ Mr.Toxy Questo perché quelle proprietà 4294968333e 4294968111sono maggiori di 2 ** 32(che è 4294967296). Quindi, non sono indicazioni di array, quindi sono ripetute nell'ordine di creazione della proprietà, piuttosto che in ordine numerico crescente - che è esattamente quello che stanno facendo nel violino, come previsto. (vedi la mia risposta)
CertainPerformance

Risposte:


4

Questo è previsto. Secondo la specifica , il metodo che scorre sulle proprietà OrdinaryOwnPropertyKeys, fa:

  1. Per ogni propria chiave di proprietà P di O che è un indice di array , in ordine di indice numerico crescente, fare

    un. Aggiungi P come ultimo elemento delle chiavi.

  2. Per ogni propria chiave di proprietà P di O che è una stringa ma non è un indice di array, in ordine cronologico crescente di creazione della proprietà, fare

    un. Aggiungi P come ultimo elemento delle chiavi.

L'ordine numerico crescente si applica solo alle proprietà che sono indicazioni di array.

Quindi, cos'è un "indice array"? Cerca ::

Un indice intero è una chiave di proprietà a valore di stringa che è una stringa numerica canonica (vedere 7.1.21) e il cui valore numerico è +0 o un numero intero positivo ≤ 2 ^ 53 - 1. Un indice di matrice è un indice intero il cui valore numerico il valore i è compreso nell'intervallo +0 ≤ i <2 ^ 32 - 1.

Pertanto, le proprietà numeriche maggiori di 2 ^ 32 non sono indicazioni di array e quindi ripetute in ordine di creazione della proprietà. Tuttavia, le proprietà numeriche che sono inferiori a 2^32 sono indici di array e sono ripetute in ordine numerico crescente.

Quindi, per esempio:

1: Indice di matrice, verrà ripetuto numericamente

10: Indice di matrice, verrà ripetuto numericamente

4294968111: Maggiore di 2 ** 32, verrà ripetuto dopo il completamento delle indicazioni dell'array, nell'ordine di creazione della proprietà

9999999999999: Maggiore di 2 ** 32, verrà ripetuto dopo il completamento delle indicazioni dell'array, nell'ordine di creazione della proprietà

Inoltre, tieni presente che, contrariamente alla credenza popolare, l'ordine di iterazione delle proprietà è garantito anche dalle specifiche, grazie alla proposta di iterazione for-in che è la fase 4.


2

Ciò ha a che fare con il modo in cui vengono attraversate le chiavi di un oggetto.

Secondo le specifiche ES6 dovrebbe essere:

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

Ciò significa che se il valore di una chiave rimane lo stesso se convertito in un numero a 53 bit senza segno e viceversa, viene trattato come un indice intero che viene ordinato in ordine numerico crescente.

In caso contrario, viene trattato come una chiave di stringa, che viene ordinata nel modo in cui sono stati aggiunti all'oggetto.

Il trucco qui è che tutti i principali browser non seguono ancora questa specifica e usano invece un indice di array che è limitato a un numero positivo fino a 2 ^ 32-1. Quindi qualsiasi cosa al di sopra di tale limite è in realtà una chiave di stringa.

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.