Wenn Sie ein JavaScript-Entwickler sind oder einer werden möchten, bedeutet dies, dass Sie die internen Mechanismen zum Ausführen von JS-Code verstehen müssen. Insbesondere ein Verständnis des Ausführungskontexts und des Aufrufstapels ist unbedingt erforderlich, um andere JavaScript-Konzepte zu beherrschen, z. B. das Erhöhen von Variablen, den Umfang und das Schließen. Das Material, dessen Übersetzung wir heute veröffentlichen, ist dem Ausführungskontext und dem Aufrufstapel in JavaScript gewidmet.

Ausführungskontext
Der Ausführungskontext ist vereinfacht ausgedrückt ein Konzept, das die Umgebung beschreibt, in der JavaScript-Code ausgeführt wird. Code wird immer in einem Kontext ausgeführt.
▍ Führen Sie Kontexttypen aus
JavaScript hat drei Arten von Ausführungskontexten:
- Globaler Ausführungskontext. Dies ist der grundlegende Standardausführungskontext. Wenn sich ein Code nicht in einer Funktion befindet, gehört dieser Code zum globalen Kontext. Der globale Kontext ist durch das Vorhandensein eines globalen Objekts gekennzeichnet, das im Fall des Browsers das
window
ist, und durch die Tatsache, dass das this
this auf dieses globale Objekt verweist. Ein Programm kann nur einen globalen Kontext haben. - Funktionsausführungskontext. Jedes Mal, wenn eine Funktion aufgerufen wird, wird ein neuer Kontext dafür erstellt. Jede Funktion hat ihren eigenen Ausführungskontext. Ein Programm kann gleichzeitig viele Kontexte zum Ausführen von Funktionen haben. Beim Erstellen eines neuen Kontexts für die Ausführung einer Funktion werden eine bestimmte Abfolge von Schritten durchlaufen, die im Folgenden erläutert werden.
- Der Ausführungskontext der
eval
. Code, der innerhalb der eval
Funktion ausgeführt wird, hat auch einen eigenen Ausführungskontext. Die eval
wird jedoch sehr selten verwendet, daher werden wir hier nicht auf diesen Ausführungskontext eingehen.
Ausführungsstapel
Der Ausführungsstapel, der auch als Aufrufstapel bezeichnet wird, ist der LIFO-Stapel, der zum Speichern von Ausführungskontexten verwendet wird, die während der Codeausführung erstellt wurden.
Wenn die JS-Engine mit der Verarbeitung des Skripts beginnt, erstellt die Engine einen globalen Ausführungskontext und platziert ihn auf dem aktuellen Stapel. Wenn ein Befehl zum Aufrufen einer Funktion erkannt wird, erstellt die Engine einen neuen Ausführungskontext für diese Funktion und platziert ihn oben auf dem Stapel.
Die Engine führt eine Funktion aus, deren Ausführungskontext sich oben im Stapel befindet. Wenn die Funktion abgeschlossen ist, wird ihr Kontext aus dem Stapel entfernt und die Steuerung wird in den Kontext übertragen, der sich im vorherigen Element des Stapels befindet.
Wir werden diese Idee anhand des folgenden Beispiels untersuchen:
let a = 'Hello World!'; function first() { console.log('Inside first function'); second(); console.log('Again inside first function'); } function second() { console.log('Inside second function'); } first(); console.log('Inside Global Execution Context');
So ändert sich der Aufrufstapel, wenn dieser Code ausgeführt wird.
Call Stack StatusWenn der obige Code in den Browser geladen wird, erstellt die JavaScript-Engine einen globalen Ausführungskontext und platziert ihn auf dem aktuellen Aufrufstapel. Wenn Sie die Funktion
first()
aufrufen, erstellt die Engine einen neuen Kontext für diese Funktion und platziert ihn oben auf dem Stapel.
Wenn die Funktion
second()
Funktion
first()
aufgerufen wird, wird für diese Funktion ein neuer Ausführungskontext erstellt und ebenfalls auf den Stapel verschoben. Nachdem die Funktion
second()
ihre Arbeit abgeschlossen hat, wird ihr Kontext vom Stapel entfernt und die Steuerung wird in den Ausführungskontext übertragen, der sich auf dem darunter liegenden Stapel befindet, dh in den Kontext der Funktion
first()
.
Wenn die Funktion
first()
wird, wird ihr Kontext aus dem Stapel entfernt und die Steuerung in den globalen Kontext übertragen. Nachdem der gesamte Code ausgeführt wurde, ruft die Engine den globalen Ausführungskontext vom aktuellen Stapel ab.
Informationen zum Erstellen von Kontexten und Ausführen von Code
Bisher haben wir darüber gesprochen, wie die JS-Engine Ausführungskontexte verwaltet. Lassen Sie uns nun darüber sprechen, wie Ausführungskontexte erstellt werden und was mit ihnen passiert, nachdem sie erstellt wurden. Insbesondere sprechen wir über die Phase der Erstellung des Ausführungskontexts und die Phase der Codeausführung.
▍ Phase der Erstellung des Ausführungskontexts
Bevor der JavaScript-Code ausgeführt wird, wird der Ausführungskontext erstellt. Bei der Erstellung werden drei Aktionen ausgeführt:
- Dieser Wert wird bestimmt und
this
(diese Bindung) ist gebunden. - Die
LexicalEnvironment
Komponente wird erstellt. - Die
VariableEnvironment
Komponente wird erstellt.
Konzeptionell kann der Ausführungskontext wie folgt dargestellt werden:
ExecutionContext = { ThisBinding = <this value>, LexicalEnvironment = { ... }, VariableEnvironment = { ... }, }
Diese Bindung
Im globalen Ausführungskontext enthält
this
einen Verweis auf das globale Objekt (wie bereits erwähnt, ist es im Browser ein
window
).
Im Kontext der Funktionsausführung hängt der Wert davon ab, wie die Funktion aufgerufen wurde. Wenn es als Methode eines Objekts aufgerufen wird, ist der Wert dieses Objekts an dieses Objekt gebunden. In anderen Fällen ist
this
an ein globales Objekt gebunden oder auf
undefined
(im strengen Modus). Betrachten Sie ein Beispiel:
let foo = { baz: function() { console.log(this); } } foo.baz(); // 'this' 'foo', 'baz' // 'foo' let bar = foo.baz; bar(); // 'this' window, //
Lexikalische Umgebung
Gemäß
der ES6-
Spezifikation ist Lexical Environment ein Begriff, der verwendet wird, um die Beziehung zwischen Bezeichnern und einzelnen Variablen und Funktionen basierend auf der Struktur der lexikalischen Verschachtelung des ECMAScript-Codes zu definieren. Die lexikalische Umgebung besteht aus einem Umgebungsdatensatz und einem Verweis auf die externe lexikalische Umgebung, die
null
.
Einfach ausgedrückt ist eine lexikalische Umgebung eine Struktur, in der Informationen über die Entsprechung von Bezeichnern und Variablen gespeichert werden. Mit "Bezeichner" ist hier der Name einer Variablen oder Funktion gemeint, und mit "Variable" ist eine Referenz auf ein bestimmtes Objekt (einschließlich einer Funktion) oder einen primitiven Wert gemeint.
In der lexikalischen Umgebung gibt es zwei Komponenten:
- Aufzeichnung einer Umgebung. Hier werden Variablen- und Funktionsdeklarationen gespeichert.
- Link zur externen Umgebung. Das Vorhandensein eines solchen Links zeigt an, dass die lexikalische Umgebung Zugriff auf die übergeordnete lexikalische Umgebung (Bereich) hat.
Es gibt zwei Arten von lexikalischen Umgebungen:
- Die globale Umgebung (oder der globale Ausführungskontext) ist eine lexikalische Umgebung ohne externe Umgebung. Der globale Umgebungsverweis auf die externe Umgebung ist
null
. In der globalen Umgebung (im Umgebungsdatensatz) sind integrierte Sprachentitäten (wie Object
, Array
usw.) verfügbar, die dem globalen Objekt zugeordnet sind. Außerdem sind vom Benutzer globale Variablen definiert. Der Wert in dieser Umgebung zeigt auf ein globales Objekt. - Die Umgebung der Funktion, in der im Umgebungsdatensatz die vom Benutzer deklarierten Variablen gespeichert sind. Der Verweis auf die externe Umgebung kann sowohl ein globales Objekt als auch eine Funktion außerhalb der betreffenden Funktion angeben.
Es gibt zwei Arten von Umgebungsdatensätzen:
- Ein deklarativer Umgebungsdatensatz, in dem Variablen, Funktionen und Parameter gespeichert sind.
- Ein Umgebungsobjektdatensatz, in dem Informationen zu Variablen und Funktionen in einem globalen Kontext gespeichert werden.
Infolgedessen wird in einer globalen Umgebung ein Umgebungsdatensatz durch einen Objektumgebungsdatensatz und in einer Funktionsumgebung durch einen deklarativen Umgebungsdatensatz dargestellt.
Beachten Sie, dass in der Umgebung der Funktion der deklarative Datensatz der Umgebung auch das
arguments
enthält, in dem die Entsprechung zwischen den Indizes und den Werten der an die Funktion übergebenen Argumente sowie Informationen zur Anzahl solcher Argumente gespeichert sind.
Die lexikalische Umgebung kann als folgender Pseudocode dargestellt werden:
GlobalExectionContext = { LexicalEnvironment: { EnvironmentRecord: { Type: "Object", // } outer: <null> } } FunctionExectionContext = { LexicalEnvironment: { EnvironmentRecord: { Type: "Declarative", // } outer: < > } }
Umgebungsvariablen
Eine variable Umgebung ist auch eine lexikalische Umgebung, in deren Umgebungsdatensatz die mit
VariableStatement
Befehlen erstellten Bindungen im aktuellen Ausführungskontext gespeichert sind.
Da die Umgebung von Variablen auch eine lexikalische Umgebung ist, besitzt sie alle oben beschriebenen Eigenschaften der lexikalischen Umgebung.
In ES6 gibt es einen Unterschied zwischen den Komponenten
LexicalEnvironment
und
VariableEnvironment
. Es besteht in der Tatsache, dass ersteres zum Speichern von Deklarationen von Funktionen und Variablen verwendet wird, die mit den Schlüsselwörtern
let
und
const
deklariert wurden, und letzteres nur zum Speichern von Variablenbindungen, die mit dem Schlüsselwort
var
deklariert wurden.
Betrachten Sie Beispiele, die veranschaulichen, was wir gerade besprochen haben:
let a = 20; const b = 30; var c; function multiply(e, f) { var g = 20; return e * f * g; } c = multiply(20, 30);
Eine schematische Darstellung des Ausführungskontexts für diesen Code sieht folgendermaßen aus:
GlobalExectionContext = { ThisBinding: <Global Object>, LexicalEnvironment: { EnvironmentRecord: { Type: "Object", // a: < uninitialized >, b: < uninitialized >, multiply: < func > } outer: <null> }, VariableEnvironment: { EnvironmentRecord: { Type: "Object", // c: undefined, } outer: <null> } } FunctionExectionContext = { ThisBinding: <Global Object>, LexicalEnvironment: { EnvironmentRecord: { Type: "Declarative", // Arguments: {0: 20, 1: 30, length: 2}, }, outer: <GlobalLexicalEnvironment> }, VariableEnvironment: { EnvironmentRecord: { Type: "Declarative", // g: undefined }, outer: <GlobalLexicalEnvironment> } }
Wie Sie wahrscheinlich bemerkt haben, haben Variablen und Konstanten, die mit den Schlüsselwörtern
let
und
const
deklariert wurden, keine zugeordneten Werte, und Variablen, die mit dem Schlüsselwort
var
deklariert wurden, werden auf
undefined
.
Dies liegt daran, dass der Code während der Erstellung des Kontexts nach Deklarationen von Variablen und Funktionen sucht, während die Deklarationen von Funktionen vollständig in der Umgebung gespeichert werden. Die Werte von Variablen werden bei Verwendung von
var
auf
undefined
, und bei Verwendung von
let
oder
const
bleiben sie nicht initialisiert.
Aus diesem Grund können Sie auf Variablen zugreifen, die mit
var
deklariert wurden, bevor sie deklariert wurden (obwohl sie
undefined
). Wenn Sie jedoch versuchen, auf Variablen oder Konstanten zuzugreifen, die mit
let
und
const
deklariert wurden, bevor sie deklariert wurden, tritt ein Fehler auf .
Was wir gerade beschrieben haben, heißt "Hubvariablen". Variablendeklarationen „steigen“ an die Spitze ihres lexikalischen Bereichs, bevor Operationen ausgeführt werden, bei denen ihnen Werte zugewiesen werden.
▍ Phase der Codeausführung
Dies ist vielleicht der einfachste Teil dieses Materials. In diesem Stadium werden die Werte den Variablen zugewiesen und der Code wird ausgeführt.
Beachten Sie, dass die JS-Engine, wenn sie während der Ausführung des Codes den Wert der mit dem Schlüsselwort
let
am Deklarationsort deklarierten Variablen nicht finden kann, dieser Variablen den
undefined
Wert
undefined
.
Zusammenfassung
Wir haben gerade die internen Mechanismen zur Ausführung von JavaScript-Code besprochen. Um ein sehr guter JS-Entwickler zu sein, ist es zwar nicht erforderlich, all dies zu wissen. Wenn Sie jedoch die oben genannten Konzepte verstehen, können Sie andere Mechanismen der Sprache besser und tiefer verstehen, z. B. das Erhöhen von Variablen, den Umfang usw. Kurzschlüsse.
Liebe Leser! Was halten Sie außer dem Ausführungskontext und dem Aufrufstapel für JavaScript-Entwickler für nützlich?
