globalThis
はグローバルオブジェクトを直接指しませんこの章では、JavaScript のグローバル変数がどのように機能するかを詳しく検討します。興味深い現象がいくつか関連しています。スクリプトのスコープ、いわゆる「グローバルオブジェクト」などです。
変数のレキシカルスコープ(短縮してスコープ)は、変数がアクセスできるプログラムの領域です。JavaScript のスコープは静的(ランタイム時には変更されません)で入れ子にできます – たとえば
if
ステートメント(行 B) によって導入されたスコープは、関数 func()
(行 A)のスコープの中に入れ子になっています。
スコープ S の最も内側の周囲のスコープは、S の外部スコープと呼ばれます。この例では、func
が if
の外部スコープになります。
JavaScript の言語仕様では、スコープは レキシカル環境を使用して「実装」されます。レキシカル環境は、次の 2 つのコンポーネントで構成されます。
変数名を変数値にマッピングする環境レコード(辞書を考えてみてください)。これが、スコープの変数の実際の格納スペースです。レコード内の名前値のエントリはバインディングと呼ばれます。
外部環境への参照。外部スコープの環境です。
したがって、入れ子のスコープのツリーは、外部環境レファレンスによってリンクされた環境のツリーで表されます。
グローバルオブジェクトは、そのプロパティがグローバル変数になるオブジェクトです。(具体的に環境のツリーにどのように収まるかについては、後で説明します。)次のグローバル変数を使用してアクセスできます。
globalThis
。この名前は、グローバルスコープで this
と同じ値を持つという事実に基づいています。window
はグローバルオブジェクトを参照するための従来の形式です。標準のブラウザコードでは機能しますが、Web Workers(通常のブラウザプロセスと同時に実行されるプロセス)や Node.js では機能しません。self
はブラウザのどこでも使用できます(Web Workers を含む)。ただし、Node.js ではサポートされていません。global
は Node.js でのみ使用できます。globalThis
は グローバルオブジェクトを直接指しませんブラウザで、globalThis
は グローバルを直接指さず、間接的な参照になります。たとえば、ウェブページの iframe を考えてみます。
src
が変更されるたびに、新しいグローバルオブジェクトを取得します。globalThis
は常に同じ値を持ちます。その値は iframe 外 から確認でき、以下で説明します(globalThis
の提案書にある例に触発されています)。ファイル 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>
ファイル iframe.html
ブラウザはこのシナリオで globalThis
が変化しないようにどのように保証しますか?ブラウザは、内部で 2 つのオブジェクトを区別します。
Window
はグローバルオブジェクトです。場所は変更されるたびに変化します。WindowProxy
は、現在の Window
へのあらゆるアクセスを転送するオブジェクトです。このオブジェクトは変更されません。ブラウザでは、globalThis
は WindowProxy
を参照し、他の場所ではグローバルオブジェクトを直接参照します。
グローバールスコープは「最上位」スコープであり、外部スコープがありません。その環境はグローバル環境です。すべての環境は、外部環境参照でリンクされた環境のチェーンを介してグローバル環境に接続されています。グローバル環境の外部環境参照は null
です。
グローバル環境レコードは、その変数を管理するために 2 つの環境レコードを使用します。
オブジェクト環境レコードは、通常の環境レコードと同じインターフェイスを持ちますが、そのバインディングは JavaScript オブジェクト内に保持されます。この場合、オブジェクトはグローバルオブジェクトです。
バインディングの独自のストレージを持っている通常の(宣言的)環境レコード。
これらの 2 つの記録のどちらがいつ使用されるかは、後で説明します。
JavaScript では、スクリプトの最上位レベルでのみグローバルスコープになります。対照的に、各モジュールにはそれ自身のスコープがあり、それはスクリプトルスコープのサブスコープです。
グローバル環境にどのように変数のバインディングが追加されるかという比較的複雑なルールを無視すると、グローバルスコープとモジュールスコープはネストされたコードブロックのように機能します。
{ // Global scope (scope of *all* scripts)
// (Global variables)
{ // Scope of module 1
···
}
{ // Scope of module 2
···
}
// (More module scopes)
}
真にグローバルな変数を作成するには、グローバルスコープにいる必要があります。これはスクリプトの最上位でのみ行われます。
const
、let
、および class
は、宣言的環境レコードにバインディングを作成します。var
と関数宣言は、オブジェクト環境レコードにバインディングを作成します。<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>
変数の取得または設定を行い、両方の環境レコードがその変数のバインディングを持っている場合、宣言的レコードが優先されます。
<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>
var
と関数宣言を介して作成された変数に加えて、グローバルオブジェクトには次のプロパティが含まれています。
const
または let
を使用すると、グローバル変数の宣言が ECMAScript とホストプラットフォームの組み込みグローバル変数に影響を与えない(または影響されない)ことが保証されます。
たとえば、ブラウザには グローバル変数 .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';
変数が既に存在する場合(この場合の location
など)、初期化子が付いた var
宣言は代入のように動作します。そのため、この例の問題が発生します。
これはグローバルスコープでのみ問題になることに注意してください。モジュールでは、グローバルスコープにはなりません( `eval()` や類似のものを使用する場合を除く)。
図 10 は、このセクションで学習したすべての内容をまとめています。
グローバルオブジェクトは通常、間違いとみなされます。そのため、 `const`、`let`、およびクラスなどの新しい構成要素は、(スクリプスコープの場合に)通常のグローバル変数を作成します。
幸いにも、最新の JavaScript で記述されたコードの大部分は、ECMAScript モジュールと CommonJS モジュール 内にあります。各モジュールには独自のスコープがあり、それが、グローバル変数を決定するルールがモジュールベースのコードにほとんど関係しない理由です。
ECMAScript 仕様における環境とグローバルオブジェクト
globalThis
:
globalThis
”this
値にアクセスするさまざまな方法: マティアス・バイネンスによる “ユニバーサル JavaScript で恐ろしい globalThis
ポリフィル”ブラウザのグローバルオブジェクト
this
をどのようにカスタマイズするかを確認できます。 セクション「InitializeHostDefinedRealm()」