Weitere Antworten auf frühere Antworten ...
Aus der Sicht eines allgemeinen Compilers und ohne Berücksichtigung von VM-spezifischen Optimierungen:
Zuerst durchlaufen wir die lexikalische Analysephase, in der wir den Code tokenisieren.
Beispielsweise können die folgenden Token hergestellt werden:
[]: ARRAY_INIT
[1]: ARRAY_INIT (NUMBER)
[1, foo]: ARRAY_INIT (NUMBER, IDENTIFIER)
new Array: NEW, IDENTIFIER
new Array(): NEW, IDENTIFIER, CALL
new Array(5): NEW, IDENTIFIER, CALL (NUMBER)
new Array(5,4): NEW, IDENTIFIER, CALL (NUMBER, NUMBER)
new Array(5, foo): NEW, IDENTIFIER, CALL (NUMBER, IDENTIFIER)
Hoffentlich sollte dies Ihnen eine ausreichende Visualisierung bieten, damit Sie verstehen, wie viel mehr (oder weniger) Verarbeitung erforderlich ist.
Basierend auf den oben genannten Token wissen wir, dass ARRAY_INIT immer ein Array erzeugt. Wir erstellen daher einfach ein Array und füllen es aus. In Bezug auf die Mehrdeutigkeit hat die lexikalische Analysephase ARRAY_INIT bereits von einem Objekteigenschafts-Accessor (z. B. obj[foo]
) oder Klammern in Zeichenfolgen / Regex-Literalen (z. B. "foo [] bar" oder / [] /) unterschieden.
Das ist winzig, aber wir haben auch mehr Token mit new Array
. Darüber hinaus ist noch nicht ganz klar, dass wir einfach ein Array erstellen wollen. Wir sehen den "neuen" Token, aber "neu" was? Wir sehen dann das IDENTIFIER-Token, das bedeutet, dass wir ein neues "Array" wollen, aber JavaScript-VMs unterscheiden im Allgemeinen kein IDENTIFIER-Token und keine Token für "native globale Objekte". Deshalb...
Wir müssen die Scope-Kette jedes Mal nachschlagen, wenn wir auf ein IDENTIFIER-Token stoßen. Javascript-VMs enthalten für jeden Ausführungskontext ein "Aktivierungsobjekt", das das Objekt "Argumente", lokal definierte Variablen usw. enthalten kann. Wenn wir es im Aktivierungsobjekt nicht finden können, suchen wir die Bereichskette, bis wir den globalen Bereich erreichen . Wenn nichts gefunden wird, werfen wir a ReferenceError
.
Sobald wir die Variablendeklaration gefunden haben, rufen wir den Konstruktor auf. new Array
ist ein impliziter Funktionsaufruf, und die Faustregel lautet, dass Funktionsaufrufe während der Ausführung langsamer sind (daher erlauben statische C / C ++ - Compiler "Function Inlining" - was JS JIT-Engines wie SpiderMonkey im laufenden Betrieb tun müssen).
Der Array
Konstruktor ist überladen. Der Array-Konstruktor ist als nativer Code implementiert, bietet also einige Leistungsverbesserungen, muss jedoch noch die Länge der Argumente überprüfen und entsprechend handeln. Darüber hinaus müssen wir den Typ des Arguments weiter überprüfen, falls nur ein Argument angegeben wird. neues Array ("foo") erzeugt ["foo"], während neues Array (1) [undefiniert] erzeugt
Um alles zu vereinfachen: Mit Array-Literalen weiß die VM, dass wir ein Array wollen. Mit new Array
muss die VM zusätzliche CPU-Zyklen verwenden, um herauszufinden, was new Array
tatsächlich funktioniert.