Alle möglichen Faktoren spielen eine Rolle. Die meisten JS-Implementierungen verwenden ein flaches Array, das in einen spärlichen Speicher konvertiert wird, wenn dies später erforderlich wird.
Grundsätzlich ist die Entscheidung, spärlich zu werden, eine Heuristik, die darauf basiert, welche Elemente festgelegt werden und wie viel Platz verschwendet würde, um flach zu bleiben.
In Ihrem Fall setzen Sie zuerst das letzte Element, was bedeutet, dass die JS-Engine ein Array sieht, das eine Länge von n
nur einem einzelnen Element haben muss. Wenn n
es groß genug ist, wird das Array sofort zu einem Array mit geringer Dichte. In den meisten Engines bedeutet dies, dass alle nachfolgenden Einfügungen den Fall eines langsamen Arrays mit geringer Dichte annehmen.
Sie sollten einen zusätzlichen Test hinzufügen, in dem Sie das Array von Index 0 bis Index n-1 füllen - es sollte viel, viel schneller sein.
Als Antwort auf @Christoph und aus dem Wunsch heraus zu zögern, finden Sie hier eine Beschreibung, wie Arrays (allgemein) in JS implementiert werden - die Besonderheiten variieren von JS-Engine zu JS-Engine, aber das allgemeine Prinzip ist dasselbe.
Alle JS Object
(also keine Zeichenfolgen, Zahlen, true, false undefined
oder null
) erben von einem Basisobjekttyp - die genaue Implementierung variiert, es kann sich um eine C ++ - Vererbung oder manuell in C handeln (dies hat beide Vorteile ) - Der Basisobjekttyp definiert die Standardmethoden für den Zugriff auf Eigenschaften, z.
interface Object {
put(propertyName, value)
get(propertyName)
private:
map properties;
}
Dieser Objekttyp behandelt die gesamte Standard-Eigenschaftszugriffslogik, die Prototypenkette usw. Dann wird die Array-Implementierung
interface Array : Object {
override put(propertyName, value)
override get(propertyName)
private:
map sparseStorage;
value[] flatStorage;
value length;
}
Wenn Sie nun ein Array in JS erstellen, erstellt die Engine etwas, das der obigen Datenstruktur ähnelt. Wenn Sie ein Objekt in die Array-Instanz einfügen, prüft die put-Methode des Arrays, ob der Eigenschaftsname eine Ganzzahl ist (oder in eine Ganzzahl konvertiert werden kann, z. B. "121", "2341" usw.) zwischen 0 und 2 ^ 32 -1 (oder möglicherweise 2 ^ 31-1, ich vergesse genau). Ist dies nicht der Fall, wird die put-Methode an die Basisobjektimplementierung weitergeleitet, und die Standardlogik [[Put]] wird ausgeführt. Andernfalls wird der Wert in den eigenen Speicher des Arrays gestellt. Wenn die Daten ausreichend kompakt sind, verwendet die Engine den Flat-Array-Speicher. In diesem Fall ist das Einfügen (und Abrufen) nur eine Standard-Array-Indizierungsoperation, andernfalls konvertiert die Engine das Array Verwenden Sie eine Karte, um von propertyName zu value location zu gelangen.
Ich bin mir ehrlich gesagt nicht sicher, ob eine JS-Engine nach dieser Konvertierung derzeit von dünnem zu flachem Speicher konvertiert.
Wie auch immer, das ist ein ziemlich allgemeiner Überblick darüber, was passiert, und lässt einige der schwierigeren Details aus, aber das ist das allgemeine Implementierungsmuster. Die Einzelheiten darüber, wie der zusätzliche Speicher und wie Put / Get versendet werden, sind von Motor zu Motor unterschiedlich - aber dies ist das klarste, was ich wirklich beschreiben kann.
Ein kleiner Zusatzpunkt, während die ES-Spezifikation propertyName
als Zeichenfolge bezeichnet wird. JS-Engines sind in der Regel auch auf die Suche nach Ganzzahlen spezialisiert. Daher someObject[someInteger]
wird die Ganzzahl nicht in eine Zeichenfolge konvertiert, wenn Sie ein Objekt mit ganzzahligen Eigenschaften betrachten, z. Array-, String- und DOM-Typen ( NodeList
s usw.).