Das Sortieren von Eigenschaften in Javascript ist fehlerhaft

15

Ich muss ein JavaScript-Objekt durchlaufen, das es als Array mit benutzerdefinierten Schlüsseln behandelt. Ich weiß, dass dies nicht vollständig unterstützt wird, da Eigenschaften keine instrumentelle Reihenfolge haben, aber da ich die Eigenschaften immer neu ordne, fand ich diesen Ansatz einfach und zuverlässig ... bis jetzt.

Das Problem tritt auf, wenn die Schlüssel Zahlen oder Zeichenfolgen sind, die als Zahlen umgewandelt werden können.

Wenn ich diesen Code ausführe:

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

Die Ausgabe ist:

4294966111
4294966222
4294966333
---
4294968222
4294968333
4294968111

Was bedeutet:

  • (test1) Wenn die Tasten unter 2 ^ 32 (4,294,967,296) liegen, werden sie automatisch neu angeordnet, die kleinste zuerst
  • (test2) Wenn die Tasten über 2 ^ 32 liegen, werden sie NICHT neu angeordnet.

Die Frage ist: Warum passiert das?

Da alle von mir getesteten Browser (Google Chrome 79.0, Mozilla Firefox 71.0, Microsoft Edge 44.18362, Internet Explorer 11.535) dieser Ausgabe zustimmen, muss es einige offizielle Spezifikationen geben.

Aktualisieren

Ich habe viele Zahlen getestet, bevor ich herausgefunden habe, dass es sich um eine Schwellenfrage handelt. Ich fand es seltsam, dass sich die Sequenz 2,3,1 anders verhält als drei Zeitstempel, die auf die gleiche Weise bestellt wurden.

Karma
quelle
Ich vermute, wie der Hash-Code berechnet wird, aber es ist keine echte Antwort auf Ihre Frage.
Mario Vernari
1
Ich glaube nicht, dass es im eigentlichen umgangssprachlichen Sinne des Wortes kaputt ist. Sie garantieren nicht, dass die Werte in der angegebenen Reihenfolge wiederholt werden, da es willkürlich ausgeführt wird, wie Sie unter developer.mozilla.org/en-US/docs/Web überprüfen können / JavaScript / Reference /… "Hinweis: for ... in sollte nicht zum Iterieren über ein Array verwendet werden, bei dem die Indexreihenfolge wichtig ist." Sie garantieren nur die Iteration über jedes Element in der Sammlung. So etwas wie forEach berücksichtigt tatsächlich die Reihenfolge, indem es Elemente in aufsteigender Reihenfolge durchläuft. Ecma-international.org/ecma-262/5.1/#sec-15.4.4.18
Mr.Toxy
Für die Aufzeichnung können Sie das Problem direkt durch Protokollieren test1und sehen test2. Ich denke, das "Problem" liegt im Schlüssel-Caching in der V8-Implementierung der Spezifikation.
Seblor
Es ist mehr so, dass unter 2 ^ 32 Ihr Eigenschaftsname zufällig ähnlich wie die internen Eigenschaftsreferenzen angeordnet ist. Sie können und sollten sich nicht auf die Reihenfolge der Objekteigenschaften verlassen, da diese per Definition nicht geordnet sind und objektbezogene Eigenschaften enthalten können. Wirf / ordne dein Objekt immer in ein Array, sortiere das Array und durchlaufe es dann, wenn die Reihenfolge wichtig ist.
user3154108
1
@ Mr.Toxy Das ist , weil diese Eigenschaften 4294968333und 4294968111sind größer als 2 ** 32(das ist 4294967296). Sie sind also keine Array-Angaben, daher werden sie in der Reihenfolge der Eigenschaftserstellung und nicht in aufsteigender numerischer Reihenfolge wiederholt - genau das tun sie erwartungsgemäß in der Geige. (siehe meine Antwort)
Bestimmte Leistung

Antworten:

4

Dies wird erwartet. Gemäß der Spezifikation führt die Methode, die über Eigenschaften iteriert, Folgendes aus OrdinaryOwnPropertyKeys:

  1. Für jeden eigenen Eigenschaftsschlüssel P von O , der ein Array-Index ist , in aufsteigender numerischer Indexreihenfolge

    ein. Fügen Sie P als letztes Schlüsselelement hinzu.

  2. Für jeden eigenen Eigenschaftsschlüssel P von O, der ein String, aber kein Array-Index ist, tun Sie dies in aufsteigender chronologischer Reihenfolge der Eigenschaftserstellung

    ein. Fügen Sie P als letztes Schlüsselelement hinzu.

Die aufsteigende numerische Reihenfolge gilt nur für Eigenschaften, die Array-Anzeigen sind.

Was ist ein "Array-Index"? Schauen Sie es oben: :

Ein ganzzahliger Index ist ein Eigenschaftsschlüssel mit einem String-Wert, der ein kanonischer numerischer String ist (siehe 7.1.21) und dessen numerischer Wert entweder +0 oder eine positive ganze Zahl ≤ 2 ^ 53 - 1 ist. Ein Array-Index ist ein ganzzahliger Index, dessen numerischer Wert Der Wert i liegt im Bereich +0 ≤ i <2 ^ 32 - 1.

Numerische Eigenschaften, die größer als 2 ^ 32 sind, sind also keine Array-Angaben und werden daher in der Reihenfolge der Eigenschaftserstellung iteriert. Allerdings numerische Eigenschaften , die weniger als 2^32 sind Array indicies und werden in aufsteigender numerischer Reihenfolge iteriert.

Also zum Beispiel:

1: Array-Index, wird numerisch durchlaufen

10: Array-Index, wird numerisch durchlaufen

4294968111: Größer als 2 ** 32, wird nach Abschluss der Array-Angaben in der Reihenfolge der Eigenschaftserstellung wiederholt

9999999999999: Größer als 2 ** 32, wird nach Abschluss der Array-Angaben in der Reihenfolge der Eigenschaftserstellung wiederholt

Auch halten Sie daran , dass, entgegen der landläufigen Meinung, Eigenschaft Iterationsreihenfolge wird durch die Spezifikation und garantiert dank den for-in - Iteration Vorschlag , das ist Stufe 4.

Bestimmte Leistung
quelle
2

Dies hängt damit zusammen, wie die Schlüssel eines Objekts durchlaufen werden.

Gemäß den ES6-Spezifikationen sollte es sein:

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

Das heißt, wenn der Wert eines Schlüssels gleich bleibt, wenn er in eine vorzeichenlose 53-Bit-Zahl und zurück konvertiert wird, wird er als ganzzahliger Index behandelt, der in aufsteigender numerischer Reihenfolge sortiert wird.

Wenn dies fehlschlägt, wird es als Zeichenfolgenschlüssel behandelt, die so angeordnet sind, wie sie dem Objekt hinzugefügt wurden.

Der Haken dabei ist, dass alle gängigen Browser dieser Spezifikation noch nicht folgen und stattdessen einen Array-Index verwenden, der auf eine positive Zahl bis beschränkt ist 2 ^ 32-1. Alles, was über dieser Grenze liegt, ist also tatsächlich ein String-Schlüssel.

obskur
quelle