Sortowanie właściwości w JavaScript jest zepsute

15

Muszę przejść przez obiekt JavaScript traktując go jako tablicę z niestandardowymi kluczami. Wiem, że nie jest to w pełni obsługiwane, ponieważ właściwości nie mają kolejności instruktażowej, ale ponieważ zawsze zmieniam właściwości, uważam to podejście za proste i niezawodne ... aż do teraz.

Problem występuje, gdy klucze są cyframi lub ciągami, które można rzutować jako liczby.

Kiedy uruchamiam ten kod:

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

dane wyjściowe to:

4294966111
4294966222
4294966333
---
4294968222
4294968333
4294968111

Co znaczy:

  • (test1) jeśli klucze są mniejsze niż 2 ^ 32 (4 294 967 296), są one automatycznie porządkowane, najpierw najmniejsze
  • (test2) jeśli klucze są powyżej 2 ^ 32, NIE zostaną ponownie uporządkowane.

Pytanie brzmi: dlaczego tak się dzieje?

Ponieważ wszystkie przeglądarki, które testowałem (Google Chrome 79.0, Mozilla Firefox 71.0, Microsoft Edge 44.18362, Internet Explorer 11.535) są zgodne co do tego wyniku, musi istnieć jakaś oficjalna specyfikacja.

Aktualizacja

Przetestowałem wiele liczb, zanim odkryłem, że to kwestia progowa. Stwierdziłem dziwne, że sekwencja 2,3,1 zachowuje się inaczej niż trzy znaczniki czasu uporządkowane w ten sam sposób.

Kar.ma
źródło
domyślam się, jak obliczany jest kod skrótu, ale nie jest to prawdziwa odpowiedź na twoje pytanie.
Mario Vernari,
1
Nie sądzę, że jest zepsuty w prawdziwym potocznym znaczeniu tego słowa, nie gwarantują, że wartości będą iterowane według kolejności, ponieważ działa on arbitralnie, jak można sprawdzić developer.mozilla.org/en-US/docs/Web / JavaScript / Reference /… „Uwaga: for… in nie należy używać do iteracji po tablicy, w której ważna jest kolejność indeksów.” Gwarantują one tylko iterację każdego elementu w kolekcji. Coś w rodzaju forEach faktycznie bierze pod uwagę kolejność, przechodząc przez elementy w porządku rosnącym ecma-international.org/ecma-262/5.1/#sec-15.4.4.18
Mr.Toxy
Dla przypomnienia możesz zobaczyć problem bezpośrednio, logując się test1i test2. Myślę, że „problem” pochodzi z kluczowego buforowania w implementacji specyfikacji V8.
Seblor,
Chodzi o to, że poniżej 2 ^ 32 twoja nazwa własności jest uporządkowana przypadkowo podobnie jak wewnętrzne odwołania do właściwości. Możesz i nie powinieneś polegać na kolejności właściwości obiektu, ponieważ z definicji nie są one uporządkowane i mogą zawierać właściwości, które są nieodłączne od obiektu. Zawsze rzutuj / zamapuj swój obiekt w tablicy, posortuj tablicę, a następnie przejdź przez nią, jeśli kolejność jest ważna.
user3154108,
1
@ Mr.Toxy to dlatego, że te właściwości 4294968333i 4294968111są większe niż 2 ** 32(co jest 4294967296). Nie są to więc tablice, więc są iterowane w kolejności tworzenia właściwości, a nie w kolejności rosnącej - to dokładnie to, co robią w skrzypcach, zgodnie z oczekiwaniami. (patrz moja odpowiedź)
CertainPerformance,

Odpowiedzi:

4

Jest to oczekiwane. Zgodnie ze specyfikacją , metoda iterująca po właściwościach OrdinaryOwnPropertyKeys:

  1. Dla każdego własnego klucza własności P z O, który jest indeksem tablicowym , w rosnącej kolejności indeksów numerycznych, wykonaj

    za. Dodaj P jako ostatni element kluczy.

  2. Dla każdego własnego klucza własności P z O, który jest ciągiem, ale nie jest indeksem tablicowym, w rosnącym porządku chronologicznym tworzenia właściwości, wykonaj

    za. Dodaj P jako ostatni element kluczy.

Rosnąca kolejność numeryczna dotyczy tylko właściwości, które są znakami tablicowymi.

Czym jest „indeks tablicy”? Sprawdź to :

Indeks liczb całkowitych to klucz właściwości o wartości ciągu, który jest kanonicznym ciągiem liczbowym (patrz 7.1.21) i którego wartość liczbowa to +0 lub dodatnia liczba całkowita ≤ 2 ^ 53 - 1. Indeks tablicy jest indeksem liczb całkowitych, którego wartość liczbowa wartość i jest w zakresie +0 ≤ i <2 ^ 32 - 1.

Tak więc właściwości liczbowe, które są większe niż 2 ^ 32, nie są wskaźnikami tablicowymi i dlatego są iterowane w kolejności tworzenia właściwości. Jednak właściwości liczbowe, które są mniejsze niż 2^32 wskaźnikami tablicowymi, są iterowane w porządku rosnącym numerycznie.

Na przykład:

1: Indeks tablicy, będzie iterowany ponad numerycznie

10: Indeks tablicy, będzie iterowany ponad numerycznie

4294968111: Większa niż 2 ** 32, będzie iterowana po zakończeniu wskazań tablicy, w kolejności tworzenia właściwości

9999999999999: Większa niż 2 ** 32, będzie iterowana po zakończeniu wskazań tablicy, w kolejności tworzenia właściwości

Należy również pamiętać, że w przeciwieństwie do powszechnego przekonania, kolejność iteracji nieruchomości jest gwarantowana również przez specyfikację, dzięki propozycji iteracji for-in, która jest etapem 4.

CertainPerformance
źródło
2

Ma to związek ze sposobem, w jaki klucze obiektu przechodzą.

Zgodnie ze specyfikacjami ES6 powinno to być:

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

Oznacza to, że jeśli wartość klucza pozostanie taka sama, jeśli zostanie przekonwertowana na 53-bitową liczbę bez znaku, a następnie traktowana jest jak indeks liczb całkowitych, który jest sortowany w porządku rosnącym numerycznie.

Jeśli to się nie powiedzie, jest traktowane jak klucz łańcuchowy, które są uporządkowane w sposób, w jaki zostały dodane do obiektu.

Problem polega na tym, że wszystkie główne przeglądarki nie stosują się jeszcze do tej specyfikacji i zamiast tego używają indeksu tablicowego, który jest ograniczony do liczby dodatniej do 2 ^ 32-1. Więc wszystko powyżej tego limitu jest tak naprawdę kluczem łańcuchowym.

niejasny
źródło