globalThis nicht direkt auf das globale ObjektIn diesem Kapitel werfen wir einen detaillierten Blick darauf, wie globale Variablen in JavaScript funktionieren. Mehrere interessante Phänomene spielen eine Rolle: der Scope von Skripten, das sogenannte globale Objekt und mehr.
Der lexikalische Scope (kurz: Scope) einer Variablen ist der Bereich eines Programms, in dem sie zugänglich ist. JavaScripts Scopes sind statisch (sie ändern sich nicht zur Laufzeit) und sie können verschachtelt sein – zum Beispiel
Der durch die if-Anweisung (Zeile B) eingeführte Scope ist in den Scope der Funktion func() (Zeile A) verschachtelt.
Der innerste umgebende Scope eines Scopes S wird als äußerer Scope von S bezeichnet. Im Beispiel ist func der äußere Scope von if.
In der JavaScript-Sprachspezifikation werden Scopes über lexikalische Umgebungen „implementiert“. Sie bestehen aus zwei Komponenten
Einem Umgebungs-Record, der Variablennamen Variablenwerten zuordnet (denken Sie an ein Wörterbuch). Dies ist der eigentliche Speicherplatz für die Variablen des Scopes. Die Namens-Wert-Einträge im Record werden Bindings genannt.
Ein Verweis auf die äußere Umgebung – die Umgebung für den äußeren Scope.
Der Baum der verschachtelten Scopes wird daher durch einen Baum von Umgebungen dargestellt, die durch Verweise auf äußere Umgebungen verbunden sind.
Das globale Objekt ist ein Objekt, dessen Eigenschaften zu globalen Variablen werden. (Wir werden bald untersuchen, wie genau es in den Baum der Umgebungen passt.) Es kann über die folgenden globalen Variablen zugegriffen werden
globalThis. Der Name basiert auf der Tatsache, dass er denselben Wert wie this im globalen Scope hat.window ist die klassische Methode, um auf das globale Objekt zu verweisen. Sie funktioniert in normalem Browser-Code, aber nicht in Web Workers (Prozesse, die parallel zum normalen Browserprozess laufen) und nicht auf Node.js.self ist in Browsern überall verfügbar (einschließlich Web Workers). Sie wird jedoch von Node.js nicht unterstützt.global ist nur auf Node.js verfügbar.globalThis nicht direkt auf das globale ObjektIn Browsern zeigt globalThis nicht direkt auf das Globale, es gibt eine Indirektion. Betrachten Sie als Beispiel einen iframe auf einer Webseite
src des iframes ändert, erhält er ein neues globales Objekt.globalThis hat jedoch immer denselben Wert. Dieser Wert kann von außerhalb des iframes überprüft werden, wie unten gezeigt (inspiriert von einem Beispiel im globalThis-Vorschlag).Datei parent.html
<iframe src="iframe.html?first"></iframe>
<script>
const iframe = document.querySelector('iframe');
const icw = iframe.contentWindow; // `globalThis` of iframe
iframe.onload = () => {
// Access properties of global object of iframe
const firstGlobalThis = icw.globalThis;
const firstArray = icw.Array;
console.log(icw.iframeName); // 'first'
iframe.onload = () => {
const secondGlobalThis = icw.globalThis;
const secondArray = icw.Array;
// The global object is different
console.log(icw.iframeName); // 'second'
console.log(secondArray === firstArray); // false
// But globalThis is still the same
console.log(firstGlobalThis === secondGlobalThis); // true
};
iframe.src = 'iframe.html?second';
};
</script>Datei iframe.html
Wie stellen Browser sicher, dass globalThis in diesem Szenario nicht geändert wird? Sie unterscheiden intern zwei Objekte
Window ist das globale Objekt. Es ändert sich, wenn sich der Standort ändert.WindowProxy ist ein Objekt, das alle Zugriffe an das aktuelle Window weiterleitet. Dieses Objekt ändert sich nie.In Browsern verweist globalThis auf den WindowProxy; überall sonst verweist es direkt auf das globale Objekt.
Der globale Scope ist der „äußerste“ Scope – er hat keinen äußeren Scope. Seine Umgebung ist die globale Umgebung. Jede Umgebung ist mit der globalen Umgebung über eine Kette von Umgebungen verbunden, die durch Verweise auf äußere Umgebungen verbunden sind. Der Verweis auf die äußere Umgebung der globalen Umgebung ist null.
Der globale Umgebungs-Record verwendet zwei Umgebungs-Records zur Verwaltung seiner Variablen
Ein Objekt-Umgebungs-Record hat die gleiche Schnittstelle wie ein normaler Umgebungs-Record, speichert seine Bindings jedoch in einem JavaScript-Objekt. In diesem Fall ist das Objekt das globale Objekt.
Ein normaler (deklarativer) Umgebungs-Record, der einen eigenen Speicher für seine Bindings hat.
Welcher dieser beiden Records wann verwendet wird, wird bald erklärt.
In JavaScript befinden wir uns nur im globalen Scope auf den obersten Ebenen von Skripten. Im Gegensatz dazu hat jedes Modul seinen eigenen Scope, der ein Unter-Scope des Skript-Scopes ist.
Wenn wir die relativ komplizierten Regeln ignorieren, wie Variablen-Bindings zur globalen Umgebung hinzugefügt werden, dann funktionieren der globale Scope und die Modul-Scopes so, als wären sie verschachtelte Codeblöcke
{ // Global scope (scope of *all* scripts)
// (Global variables)
{ // Scope of module 1
···
}
{ // Scope of module 2
···
}
// (More module scopes)
}Um eine wirklich globale Variable zu erstellen, müssen wir uns im globalen Scope befinden – was nur auf der obersten Ebene von Skripten der Fall ist
const, let und class erstellen Bindings im deklarativen Umgebungs-Record.var und Funktionsdeklarationen erstellen Bindings im Objekt-Umgebungs-Record.<script>
const one = 1;
var two = 2;
</script>
<script>
// All scripts share the same top-level scope:
console.log(one); // 1
console.log(two); // 2
// Not all declarations create properties of the global object:
console.log(globalThis.one); // undefined
console.log(globalThis.two); // 2
</script>Wenn wir eine Variable abrufen oder setzen und beide Umgebungs-Records eine Bindung für diese Variable haben, dann gewinnt der deklarative Record
<script>
let myGlobalVariable = 1; // declarative environment record
globalThis.myGlobalVariable = 2; // object environment record
console.log(myGlobalVariable); // 1 (declarative record wins)
console.log(globalThis.myGlobalVariable); // 2
</script>Zusätzlich zu den über var und Funktionsdeklarationen erstellten Variablen enthält das globale Objekt die folgenden Eigenschaften
Die Verwendung von const oder let garantiert, dass globale Variablendeklarationen die eingebauten globalen Variablen von ECMAScript und der Host-Plattform nicht beeinflussen (oder von ihnen beeinflusst werden).
Zum Beispiel haben Browser die globale Variable .location
// Changes the location of the current document:
var location = 'https://example.com';
// Shadows window.location, doesn’t change it:
let location = 'https://example.com';Wenn eine Variable bereits existiert (wie in diesem Fall location), dann verhält sich eine var-Deklaration mit einem Initialisierer wie eine Zuweisung. Deshalb geraten wir in diesem Beispiel in Schwierigkeiten.
Beachten Sie, dass dies nur im globalen Scope ein Problem darstellt. In Modulen befinden wir uns nie im globalen Scope (es sei denn, wir verwenden eval() oder ähnliches).
Abb. 10 fasst alles zusammen, was wir in diesem Abschnitt gelernt haben.
Das globale Objekt wird generell als Fehler angesehen. Aus diesem Grund erstellen neuere Konstrukte wie const, let und Klassen normale globale Variablen (wenn sie sich im Skript-Scope befinden).
Glücklicherweise lebt der Großteil des in modernem JavaScript geschriebenen Codes in ECMAScript-Modulen und CommonJS-Modulen. Jedes Modul hat seinen eigenen Scope, weshalb die Regeln für globale Variablen bei Modul-basiertem Code selten eine Rolle spielen.
Umgebungen und das globale Objekt in der ECMAScript-Spezifikation
globalThis:
globalThis“this-Wert zuzugreifen: „Ein schrecklicher globalThis Polyfill in universellem JavaScript“ von Mathias BynensDas globale Objekt in Browsern
this anpassen: Abschnitt „InitializeHostDefinedRealm()“