👻globalThis👻と🌏global🌏と🌝this🌝

皆さんこんにちは。今回はJavaScriptの👻globalThis👻について解説しようと思います。globalThisはJavaScript (ECMAScript) の新機能です。現在TC39プロセスのStage 3にあり[^tc39_process]、このまま何事もなければ近いうちにECMAScript[^ecmascript]に正式採用されることになります。

[^ecmascript]: 今さらな説明ですが、ECMAScriptというのはざっくり言うとJavaScriptを策定している標準仕様の名前です。この記事では、ブラウザとかnode.jsとかで動く実際のJavaScriptと対比して、仕様として定められたJavaScriptの挙動に触れたいときにECMAScriptという言葉を使っています。

[^tc39_process]: 詳細は他のもっと詳しい記事に譲りますが、ECMAScriptに新機能が追加されるにはStage1 〜 Stage 4という4つの段階を経る必要があります。Stage 3は、機能の仕様が固まり、ブラウザ等に試験的に実装されるのを待っている状態です。

この記事は[JavaScript2 Advent Calendar 2018](https://qiita.com/advent-calendar/2018/javascript_02)の5日目の記事です。

さて、皆さんはこのglobalThisを使ったことがあるでしょうか。**私はありません**。<del>というか、現在のところglobalThisが利用可能なのは、つい今日安定版がリリースされたばかりのGoogle Chrome 71のみです。この記事にはglobalThisを使ったコード例が出てきますが、実際に動かしたい場合はちゃんとGoogle Chromeが最新版になっているか確認してくださいね。
（Chrome 71がリリースされるのは日本時間で今日（12月5日）のはずですが、あなたがこの記事を読むタイミングによってはまだ出ていないかもしれません。その場合は出るまで待つかベータ版をインストールしてください。）</del>

<del>※ 追記（2019-02-06）：現在のところ、Google ChromeとFirefoxの最新版でglobalThisがサポートされています。</del>

※ 追記（2019-04-26）：現在のところ、Google Chrome・Firefox・Safariの最新版、そしてnode.js v12でglobalThisがサポートされています。

# globalThisとは何か
一言でいうと、**globalThisはグローバルオブジェクト**です。グローバルオブジェクトというのは、グローバル変数をプロパティに持つようなオブジェクトです。つまり、グローバル変数を作ると、それがグローバルオブジェクトのプロパティとなって現れるのです。

```js:グローバルオブジェクトの例
// グローバル変数fooを作成（
foo = 123;
// globalThis.fooが123になっている
console.log(globalThis.foo); // 123

// 逆も可能
globalThis.bar = 456;
console.log(bar); // 456
```

余談ですが、何も付けないでいきなり作った変数と`var`を使って宣言した変数はグローバル変数になりますが、`let`や`const`で作った変数はグローバル変数になりません。やっぱり`let`や`const`は偉いですね。

```js
foo = 123;
console.log(globalThis.foo); // 123
var bar = 456;
console.log(globalThis.bar); // 456
let hoge = 7;
console.log(globalThis.hoge); // undefined
const fuga = 8;
console.log(globalThis.fuga); // undefined
```

## ブラウザにおけるグローバルオブジェクト

ところで、ここまで読んだ一部の方は「**あれ？**」と思ったのではないでしょうか。というのも、皆さんがお使いのブラウザには既に`window`が備わっていますよね。JavaScriptでグローバルオブジェクトといえば`window`のはずです。実際、グローバル変数を作ると`window`のプロパティになることは古くから知られています。

```js:windowの例
foo = 123;

console.log(window.foo); // 123
```

種を明かしてしまうと、`globalThis`は`window`と同じです。

```js
console.log(globalThis === window); // true
```

とはいえ、「**じゃあglobalThisとかいらないじゃん**」などとは今さら思いませんよね。`window`はブラウザ（DOMが実装されている環境）にしかありませんが、今やJavaScriptが動作する環境はそれだけではありません。

## node.jsにおけるグローバルオブジェクト
ブラウザ以外のJavaScript環境として代表的なのがnode.jsです。このnode.jsには`window`は存在しません。

その一方で、node.jsにはもともと`global`があり、これがグローバルオブジェクトの役割を果たしていました。

```js:node.jsにおけるグローバルオブジェクト
foo = 123;

console.log(global.foo); // 123
```

まだnodeに`globalThis`は実装されていませんが、[globalThisを実装するPR](https://github.com/nodejs/node/pull/22835/files/c26f2bd7ed071d836e76127a9068a7feea5b1b51)を見るに、やはり、というか当然ながら、`global === globalThis`となるようです。

## `this`からグローバルオブジェクトを取得する方法
さて、ここまでで記事タイトルのうち`globalThis`と`global`が出てきましたが、`this`は何の関係があるのでしょうか。

これもJavaScriptに関してよく知られた事実ですが、トップレベルの（関数の中でない）コードや、関数を普通に（オブジェクトのメソッドとしてではなく）呼んだ場合は`this`がグローバルオブジェクトになることが知られています。

```js:thisがグローバルオブジェクトになる例
foo = 123;
console.log(this.foo); // thisがグローバルオブジェクトなのでthis.fooは123
showFoo();

function showFoo() {
  // 関数の中でもthisはグローバルオブジェクト
  console.log(this.foo);
}
```

ただし、strictモードの場合は関数の中では`this`はグローバルオブジェクトではなく`undefined`になるのでこの方法は使えません。

```js
foo = 123;
showFoo();

function showFoo() {
  'use strict';
  // thisがundefinedなのでこれはエラー
  console.log(this.foo);
}
```

その一方、グローバルの（関数の中ではない）`this`はstrictモードでもグローバルオブジェクトとして利用可能です。

```html
<script>
  'use strict';
  var foo = 123;
  // トップレベルのthisはstrictモードでもグローバルオブジェクト
  // として利用可能なので123が表示される
  console.log(this.foo); 
  showFoo();

  function showFoo() {
    // 関数の中ではthisはundefinedなのでエラー
    console.log(this.foo);
  }
</script>
```

ただ裏技があって、`Function`コンストラクタを使うことでstrictモードのコンテキスト内でもstrictモードでない関数を作ることができます。

```html
<script>
  'use strict';
  var globalObj = (new Function('return this;'))();
  console.log(globalObj === globalThis); // true
</script>
```

しかしそうはいっても、この裏技がうまく動かない環境が少数存在します。[具体的にはChrome Appsで使えないそうです](https://github.com/paulmillr/es6-shim/issues/301)[^chrome_apps]。あと動的に関数を作るのが気持ち悪いですね。

[^chrome_apps]: 。ただ、Chrome Appsは今はChrome OSのみが対象らしいですが。

まとめると、グローバルオブジェクトにアクセスする既存の方法は一長一短で、完璧なものはありませんでした。

- `this`はstrictモードの関数内では使用不可。また、実はnode.jsでは`this`はグローバルオブジェクトではない。
- `(new Function('return this'))()`はChrome Appsで使用不可。
- `window`はブラウザ環境のみ。
- `global`はnode.jsのみ。

## そして`globalThis`
上のリストの上2つは標準で定められた方法ですが使える状況が限られますし、下の2つはECMAScript標準に含まれていないためは環境によって使えるものが異なり統一されていないというのが現状でした。その一方で、今の時代、全ての環境で共通のJavaScriptコードを動かしたい場面はよくあります。（この概念は時代によって呼ばれ方がいろいろと分かれているような気がしますが、自分はIsomorphic JavaScriptとか呼ぶのがしっくり来ます。）

そこで、ECMAScript標準として統一された方法を用意しようということで登場したのが`globalThis`なのです。

`globalThis`の概念自体は要するにグローバルオブジェクトなので簡単ですね。ただ、これを読んでいるあなたは、👻`globalThis`👻という名前はどうなのと思ったのではないでしょうか。私は思いました。JavaScriptが嫌いな人たちによるネガティブキャンペーンの対象になっているのをよく見る`this`を全面に押し出してくるのはなかなか攻めたネーミングです。そもそも、`globalThis`という名前を見ても何が言いたいのかよく分かりません。

まあ、とはいえ、気持ちは分からないでもありません。`this`はトップレベルの（関数内でない）環境ではグローバルオブジェクトですから、「そのような（グローバルな）環境での`this`を参照できるもの」ということで`globalThis`という命名なのでしょう。

---

ただ、ここでひとつ言えることは、`globalThis`は実は次善策だということです。もともとは、グローバルオブジェクトを表す変数の名前は`global`にする予定でした。つまり、既に存在していたnode.jsの仕様に合わせようとしたのです。

ところが、実際に`global`を実装してみると、動かなくなるウェブサイトがいくつか発生してしまいました。例えば[このスレッド](https://bugzilla.mozilla.org/show_bug.cgi?id=1326032)では、`global`が存在すればnode.js環境であると判断してnode.js向けの処理を行うようなコードが存在することが明らかとなり、それが原因でウェブサイトが動作しなくなった事例が報告されています。
ちょっと本題からずれますが、まったく**ふざけたコード**を書く人もいたものですね。これは`global`を「グローバルオブジェクト」として使うのではなく「node.jsかどうか判定するためのフラグ」として使用するコードを書いたために起こった問題です。`global`ではなく`process`を使用するコードなども目にしますが将来が危ぶまれますね。

話を戻すと、JavaScriptは後方互換性が重要な言語です。ですから、実際にウェブサイトが壊れてしまう事例がいくつも発生したことで`global`の採用は頓挫し、別の名前を探さなければいけなくなりました。[新しい名前に関するわちゃわちゃした議論をGitHubで見ることができます](https://github.com/tc39/proposal-global/issues/20)。

これのおかげで`global`の標準化は約2年も遅れたわけですが、何だかんだで`globalThis`という名前に決まり、Chrome 71でついに日の目を見ることになったわけです。`globalThis`という名前については[プロポーザル](https://github.com/tc39/proposal-global)に少し説明があります。

> After some data-gathering to determine web compatibility of a short list of choices, we settled on globalThis, as this name is both highly likely to be web compatible, and also maps to the concept of being the “global this value” (and not the “global object”, per [above](https://github.com/tc39/proposal-global#html-and-the-windowproxy)).

まあ要するに先ほど述べたような、グローバルな`this`の値だから`globalThis`なんだよということが書いてありますね。

# まとめ

ここまで述べたことはだいたい[globalThisのプロポーザル](https://github.com/tc39/proposal-global)に書いてあります。既にこれを読んでいた方にとっては新鮮味がない内容だったかもしれませんが、まあそのような方は1行目で既に察したと思うので大丈夫でしょう。

要するに、グローバルオブジェクトを扱う既存の方法はどれも一長一短で統一された方法が無かったので、全ての環境で使える`globalThis`を作りましたということです。現在のところこれが利用可能なのはGoogle Chromeのみですが、他のブラウザやnode.jsも近いうちに追随するのではないかと思います。

とはいえ、実務上は古い環境も考慮しなければならないので`globalThis`だけ使えば他は要らない、というわけにはいかないのがつらいところです。むしろ`globalThis`対応が加わった結果コードが複雑化するかもしれません。

皆さんももしグローバルオブジェクトを扱う機会があれば、`globalThis`のことを思い出してあげてください。

# 余談
今回は余談をまとめの後に持ってきてみました。以降、仕様書という場合は[ECMAScript® 2018 Language Specification](https://www.ecma-international.org/ecma-262/9.0/index.html#sec-organization-of-this-specification)を指すものとします。（`globalThis`はES2018には入っていませんがまあ大丈夫です。）

## モジュールシステムとグローバルオブジェクト
ところで、グローバル変数って難しい概念ですよね。ブラウザ上のJavaScriptでは、何も宣言せずにいきなり代入された変数や、関数の外で`var`で宣言された変数がグローバル変数となります。前者はstrictモードでは使えませんが。

下のコードで確かめてみると、`var`で宣言した変数`foo`がグローバルオブジェクトのプロパティとして出現していることが分かりますね。

```html
<script>
  'use strict';
  var foo = 123;
  console.log(globalThis.foo); // 123
</script>
```

……。

ここで、「**いや、違うでしょ**」と思った人はかなりJavaScriptに詳しいですね。「当たり前のことを何を今さら」と思った人は修行不足です。

そう、さっきの説明には実は誤っているところがあります。どこが違うのかお分かりでしょうか。

答えは以下のコードです。

```html
<script type="module">
  'use strict';
  var foo = 123;
  console.log(globalThis.foo); // undefined
</script>
```

なんと、script要素に`type="module"`属性を付けたら結果が変わりました。ブラウザ上でのJavaScriptを知っている方は、`type="module"`というのが`import`文を使うときに必要なものであることはお分かりだと思いますが、ECMAScript的にはこれは内部のコードを*Script*ではなく*Module*として評価するようにする効果があります。実はECMAScriptではプログラムは*Script*と*Module*の2種類に分類されており、`export`文や`import`文は*Module*の中でしか使えない構文です。普通の`script`要素で読みこまれるような従来のJavaScriptは*Script*です。

そして、トップレベルの`var`はScriptではグローバル変数を作る一方、Moduleではそうではないのです。なお、*Module*の中では自動的にstrictモードになるため、無宣言で変数に代入してグローバル変数を作ることはできません。グローバル変数を作りたければ、明示的にグローバルオブジェクトを介する必要があります。

この挙動はよくよく考えてみれば当然ですね。モジュールに分けられたプログラムにおいて、各モジュールは独立している（独立した変数スコープを持っている）べきです。モジュール内部で`var`で作った変数がグローバル変数になって気軽に他に影響を与えてもらっては困ります。

言葉だけだと分かりにくいかもしれませんので、例で説明します。`index.html`と`mod.js`からなるプログラムを考えましょう。

```html:index.html
<script type="module">
  import { setFoo } from './mod.js';

  var foo = 0;
  setFoo(123456);
  console.log(foo);
</script>
```

```js:mod.js
var foo;

export function setFoo(value) {
  foo = value;
}
```

この例で、`index.html`の`console.log`で表示される値はなんでしょうか。

答えは0です。123456ではありません。これはまさに、`mod.js`内で定義された変数`foo`がグローバル変数ではない（モジュールのスコープ内の変数になっている）ことの証左であることがお分かりになるかと思います。

このように、モジュールに分けられたJavaScriptではモジュール間で変数のスコープを分けることでモジュール間の独立性を高めています。

ところで、次の実験として`mod.js`を変えて次のようにしてみましょう。

```html:index.html
<script type="module">
  import { setFoo } from './mod.js';

  var foo = 0;
  setFoo(123456);
  console.log(foo);
</script>
```

```js:mod.js
export function setFoo(value) {
  globalThis.foo = 123456;
}
```

この場合は`console.log`で何が表示されるでしょうか。

答えはやっぱり0です。「123456だろ」と思った方はちょっと前からもう一度読みなおすといいかもしれません。

### 仕様書で確かめる
今説明した諸々を仕様書で確かめてみたいという方向けの説明も用意しました。まず*Script*におけるグローバル変数の初期化は[15.1.11 Runtime Semantics: GlobalDeclarationInstantiation](https://www.ecma-international.org/ecma-262/9.0/index.html#sec-globaldeclarationinstantiation)で行なわれます。ステップ7でVarScopedDeclarationsを、ステップ15でLexicallyScopedDeclarationsを計算していますが、前者が`var`で宣言された変数たち、後者が`let`や`const`で宣言された変数たちに相当します。後者の処理はステップ16で行なわれており、流し読みすると`envRec.CreateMutableBinding`が呼ばれています。これは要するに、現在のスコープに変数を作成するということです。この「現在のスコープ」は実はグローバルスコープ（グローバルオブジェクトのプロパティとして現れるグローバル変数たちのスコープ）ではないということがポイントです。

一方、`var`で宣言された変数たちの処理はステップ18で行なわれています。こちらは`envRec.CreateGlobalVarBinding`で処理されており先ほどとは違いますね。これを使った場合はグローバル変数（グローバルオブジェクトのプロパティ）として登録されます。ここまでをまとめて例で確かめてみましょう。

```html
<script>
// 実行開始時点で上記のGlobalDeclarationInstatiationは
// 行なわれているので、グローバルオブジェクトに下で宣言されている変数fooが存在
console.log('foo' in globalThis); // true

// var宣言が実際に実行されるまで値はundefined
console.log(globalThis.foo); // undefined

var foo = 123;
// fooに代入されたのでglobalThis.fooも当然書き換わる
console.log(globalThis.foo); // 123

// globalThis.fooへの代入は反映される
globalThis.foo = 99999;
console.log(foo); // 99999

let bar = 345;
// letで宣言した変数はグローバル変数ではない
console.log('bar' in globalThis); // false

// グローバル変数のbarを書き換えてもこの変数barには影響しない
globalThis.bar = 99999;
console.log(bar); // 345

// 逆も同様
bar = -5;
console.log(globalThis.bar); // 99999
</script>
```

一方で*Module*の実行時は何が起こっているでしょうか。それを確かめるには[15.2.1.16.4 Instantiate() Concrete Method](https://www.ecma-international.org/ecma-262/9.0/index.html#sec-moduledeclarationinstantiation)から参照されている[15.2.1.16.4.2 ModuleDeclarationEnvironmentSetup](https://www.ecma-international.org/ecma-262/9.0/index.html#sec-moduledeclarationenvironmentsetup)を見ます。さっきのGlobalDeclarationInstantiationとちょっと似たようなことが書いてありますね。

ただし、`var`で宣言された変数たちを処理する部分であるステップ12を見ると、CreateGlobalVarBindingではなくCreateMutableBindingが使われていますね。これが先ほどの*Script*の場合との違いであり、*Module*の場合に`var`で宣言した変数がグローバル変数とはならない（モジュールのトップレベルスコープの変数となる）ことの証左です。

```html
<script type="module">
// 先ほどとは異なり、varで宣言した変数がグローバルオブジェクトに存在しない
var foo = 123;
console.log('foo' in globalThis); // false

// globalThis.fooに何かを代入してもこの変数fooには影響しない
globalThis.foo = 99999;
console.log(foo); // 123
</script>
```

このように、*Module*では`var`で宣言した変数がグローバル変数となりません。このことは注意していないと罠にはまることがあるかもしれません。

## node.jsのモジュールシステム
では、仕様書からは離れて次の話題に移りましょう。JavaScriptをばりばり書く方の中でも、前節で取り上げたES Modules（`import`とか`export`とかを使うモジュールシステム）にはあまり馴染みがないという方がいるかもしれません。node.js用のJavaScriptプログラムにおいてはまだまだ**CommonJS Modules**が現役です。`require`を使ってモジュールを読み込むやつですね。node.jsもES Modulesの対応を進めていますが、v10でやっと試験実装が利用可能になった段階です。

そして、node.jsにおける各モジュールは、*Script*か*Module*かの分類でいえば*Script*となります。じゃあ、トップレベル`var`で変数を作るとどうなるでしょうか。さっそく試してみます。（動作を確認できるように`globalThis`の代わりに`global`を使用しています。）

```js:node.js
console.log('foo' in global); // false

var foo = 123;
console.log(global.foo); // undefined
```

**……？**

なんか、先ほどの説明と違いますね。`var`で宣言した変数がグローバル変数になっていません。

これは実はnode.jsの挙動の特徴です。細かいことは省略しますが、各ファイル（モジュール）は暗黙のうちに関数に囲まれているため、`var`で宣言した変数はグローバルスコープではなくその関数スコープに属することになります。これにより、各ファイルで`var`で宣言した変数がコンフリクトすることが無くなるという利点があるほか、`__dirname`等の実装にも寄与しているらしいです。

---

ところで、今回の話題に関連するnode.jsの特徴的な挙動はもう1つあります。それがトップレベルでの`this`です。

実は、node.js環境においてはトップレベルの（先ほど説明したようにnode.jsでの各モジュールは関数に囲まれるので厳密にはトップレベルではありませんが）`this`はグローバルオブジェクトではありません。その代わりに`exports`オブジェクトが`this`に入っています。

```js
// これはnode.jsではtrue
console.log(this === exports);
```

`exports`というのはCommonJS Modules由来のものです。まだちゃんと調べていませんが、ES Modulesモードではまた違った結果になることでしょう。*Module*コンテキストのトップレベルでは`this`は`undefined`ですから、多分`undefined`だと思います。`globalThis`が聞いて呆れますね。

## `globalThis`はグローバルオブジェクトではない！？
ちょっと話は変わりますが、まとめの直前くらいにプロポーザルから引用した文を再掲します。

> After some data-gathering to determine web compatibility of a short list of choices, we settled on globalThis, as this name is both highly likely to be web compatible, and also maps to the concept of being the “global this value” (**and not the “global object”, per [above](https://github.com/tc39/proposal-global#html-and-the-windowproxy)**).

最後の部分（筆者が太字で強調）に興味深いことが書いてあります。読んでみると、**`globalThis`はグローバルオブジェクトじゃないよ**という衝撃的なことを述べていますね。なんと、この記事でさんざん`globalThis`はグローバルオブジェクトだよと言ってきたのは嘘だったのです。

とはいえ別に実務上なにか問題があるわけではありません。グローバルオブジェクトだと思って安心して使ってください。最後に、これが言いたいのは何かということを解説したいと思います。

`above`のところのリンクの先ではなにやら`WindowProxy`というワードが出てきていますね。これはブラウザ上のJavaScriptにおける`window`オブジェクトのことを指しています。

ここでポイントとなるのは、**JavaScriptでは他のページ**（より正確にはブラウジングコンテキスト）**のwindowオブジェクトを取得できる**という点です。これには、`window.open`を使用するとか、`iframe`要素の`contentWindow`を使用するなどの方法があります。また、当然ながら同一オリジンポリシーの影響を受けます。

これはブラウジングコンテキストに対して紐付いているオブジェクトですから、参照されているブラウジングコンテキスト内でページが移動してもWindowオブジェクトは健在です。このことを確かめられるテストページを用意したので、PCの方は開きながら続きを読んでください。また、ソースコードは適宜参照してください。（スマートフォン等だと複数タブを行き来するのが面倒かもしれません。また、ソースコードを見ないと何を言っているかいまいち分からないかもしれません。）

- [テストページ](https://happy-pare-1cecc8.netlify.com/)

テストページを開くと`index.html`が表示されます。「foo.htmlを開く」ボタンを押すと`foo.html`が新しいタブで開かれます。ソースコードを見ると、`window.open`の結果（`foo.html`のWindowオブジェクト）が（`index.html`内の）`fooWindow`変数に代入されることが分かります。

この`fooWindow`変数は、今開かれた`foo.html`内における`window`オブジェクトと同じです。そのことは、`fooWindow`を通じて`foo.html`内のグローバル変数を参照したり操作できることから分かります。

`foo.html`を見るとグローバル変数`foo`に`123`が代入されていることが分かります。一方で`index.html`にある「fooの値を表示」ボタンを押すと`fooWindow.foo`が表示されますが、この結果は`123`となります。確かに`foo.html`内のグローバル変数`foo`が参照できていますね。

さらに、`foo.html`内にある「fooの値を変更」ボタンを押すとグローバル変数`foo`の値が`123456`になります。その後、`index.html`に戻って再度「fooの値を表示」ボタンを押すと今度は`123456`が表示されます。このことからも、`fooWindow.foo`が`foo.html`の中のグローバル変数`foo`そのものであることが分かります。

問題はここからです。ブラウザのタブというのはウェブページを表示しており、ウェブページはリンクをたどって別のページに移動することができます。今回は、`foo.html`には`bar.html`へのリンクが用意してあります。では、`bar.html`に移動してみてください。

皆さんは当然ご存知かと思いますが、別々のページでJavaScriptの実行状態が共有されることはありません。別のページに移動した時点で全ての状態はリセットされます。当然、`window`も違うものになるしグローバル変数も全部リセットされます。このことを確かめるために`index.html`に戻って「fooの値を表示」ボタンを押してみましょう。そうすると、結果は`undefined`となります。これは、`fooWindow.foo`が無いことを示しており、`bar.html`内にグローバル変数`foo`が無いことと対応しています。

ここで矛盾が発生していることにお気づきでしょうか。`index.html`内の変数`fooWindow`は常に同じドキュメントです。これは、`index.html`に`fooWindow`に再代入するコードが無いことから明らかです。

それにも関わらず、`foo.html`から`bar.html`に移動した時点で`fooWindow.foo`の値が変わりました。つまり、`fooWindow`は`foo.html`のグローバルオブジェクトだったのに、`bar.html`のグローバルオブジェクトに変化したということです。これは、`foo.html`と`bar.html`は別々のページなので別々のグローバルオブジェクトを持つという事実と矛盾しています[^global_object]。

[^global_object]: この事実については理由を説明していませんでしたが、HTMLの仕様でそのように定義されています。別のHTML文書への[ナビゲーション](https://html.spec.whatwg.org/multipage/browsing-the-web.html#navigate)が発生すると、[HTML文書をロードする過程](https://html.spec.whatwg.org/multipage/browsing-the-web.html#read-html)で[新しいDocumentが作成され](https://html.spec.whatwg.org/multipage/browsing-the-web.html#initialise-the-document-object)、その過程でグローバルオブジェクトとして新しいWindowオブジェクトが作られることが明記されています。

要約すると、`index.html`から見ると`fooWindow`に入っているオブジェクトは変わっていないのに、実際はページ遷移の過程で別のグローバルオブジェクトに変わっているという矛盾です。

この矛盾を解消するために導入されるのが`WindowProxy`です。`WindowProxy`は内部に`Window`オブジェクトへの参照を持っていて、基本的に`WindowProxy`への操作は内部の`Window`への操作となります[^window_proxy]。今回の例では`fooWindow`は`WindowProxy`であり、最初は内部で`foo.html`の`Window`オブジェクトとつながっています。ですから、`fooWindow`からの読み込みは`foo.html`の`Window`オブジェクトからの読み込みとなりなります。

[^window_proxy]: ただし、オリジンをまたぐ場合は制限がかかります。これは`WindowProxy`の定義で規定されています。

`foo.html`が`bar.html`にページ遷移すると、`fooWindow`が内部に持っている`Window`オブジェクトへの参照が`bar.html`の`Window`オブジェクトへと差し替わります。これはあくまで内部的な動作であり、`fooWindow`の`WindowProxy`オブジェクトとしての同一性は保ったままです。しかし、この時点で`fooWindow`への操作は内部の`Window`オブジェクト、すなわち`bar.html`の`Window`オブジェクトへの操作となるため、実際に操作される`Window`オブジェクトが最初と異なっています。

この2層構造により、前述の矛盾が解消されることになります。結局のところ何が言いたかったのかというと、「`window`というのは実は`WindowProxy`のことであってグローバルオブジェクトたる`Window`ではないから、今回作る`globalThis`をグローバルオブジェクトと呼ぶのは憚られる。なので`globalThis`になった」という話でした。

長々と説明しましたが、なんだかとてもややこしいですね。ここでちょっと話を戻して、`globalThis`のプロポーザルから一文引用します。

> ES6/ES2015 does not account for the Window/WindowProxy structure, and simply refers to ”the global object” directly. This specification does the same. 
>
> （超意訳） なんかHTMLの人たちは`Window`とか`WindowProxy`とか言ってるけど別にECMAScript的にはそういうのどーでもいいんで、面倒くさいから単に「グローバルオブジェクト」ってことにしてます。

というわけで、一応名前決めるときに気をつけたとはいえ、全くもってどうでもいい話でした。まさに余談ですね。