やあ。今日もコードと格闘しているかな?
中級クラスのエンジニアになると、「なんとなく動く」から「なぜそう動くのか」を突き詰めたくなる時期だ。今日はJavaScriptの深淵、特に初心者が魔法だと思っている「グローバルスコープ」と「windowオブジェクト」の歪な関係について話をしよう。
ここを理解しておかないと、大規模なフロントエンド開発で「なぜか変数が汚染される」「予期せぬ挙動が起きる」という地獄のバグに時間を溶かすことになる。
—
1. 「グローバルスコープ」という幻想を捨てる
多くのエンジニアが `var` を悪者扱いし、「とにかく `const` や `let` を使え」と教わってきたはずだ。だが、その理由を仕様レベルで説明できるだろうか?
結論から言えば、現代のJavaScriptにおいて「グローバル環境レコード」と「windowオブジェクト」は別物だ。ここが混乱の元凶だ。
ES6以前の古い世界
かつて、`var` を使ってグローバルで宣言した変数は、自動的に `window` オブジェクトのプロパティとして追加されていた。これは「グローバル環境レコード = windowオブジェクト」だったからだ。
ES6以降の現代
仕様上、グローバル環境レコードは「宣言的環境レコード(`const`/`let`用)」と「オブジェクト環境レコード(`var`や関数宣言用)」に分断された。これが何を意味するか。
- `const` / `let`: グローバル環境レコードに格納されるが、`window` オブジェクトには付与されない。
- `var`: 相変わらず `window` オブジェクトのプロパティとしてねじ込まれる。
—
2. 現場で遭遇する「罠」をコードで見る
まずは、この挙動をブラウザのコンソールで確認してみてほしい。
// 【実験】var と const の挙動の違いを比較する
// 1. var で宣言すると、グローバルオブジェクト(window)のプロパティになる
var legacyVar = “僕は window に住んでいます”;
console.log(window.legacyVar); // “僕は window に住んでいます”
// 2. const / let はグローバル環境レコードには属するが、window には属さない
const modernConst = “僕は window には住んでいません”;
console.log(window.modernConst); // undefined (ここが重要!)
// 3. なぜこれが大事か?
// 外部ライブラリが window を走査して何かを探すとき、
// const で宣言した変数は「存在しないもの」として扱われるため、安全性が高い。
この「windowを汚さない」という性質が、現代のフロントエンド開発において `const`/`let` が推奨される最大の理由の一つだ。`var` を使うと、意図せず `window` の既存プロパティ(例えば `name` や `location` など)を上書きしてしまい、ライブラリがクラッシュする、なんてことは往々にして起こる。
—
3. 「巻き上げ」の正体を見極める
`var` は宣言の前に参照すると `undefined` になる。対して `const`/`let` は `ReferenceError` になる。
これは、`const`/`let` には「一時的死域 (Temporal Dead Zone: TDZ)」が存在するからだ。
console.log(myVar); // undefined (巻き上げにより存在は知っているが、値は空)
var myVar = 10;
// console.log(myConst); // ReferenceError: Cannot access ‘myConst’ before initialization
// const / let は宣言位置まで「触ってはいけない」という強い規約で守られている
const myConst = 20;
このTDZのおかげで、私たちは「初期化される前に使ってしまう」という初歩的なミスを即座に検知できる。`var` のような「なんとなく使えてしまう」曖昧さは、大規模開発における最大の敵だ。
—
4. チーフアーキテクトからのアドバイス
実務の現場では、以下の運用ルールを徹底することをお勧めする。
1. `var` はコードベースから抹殺する: ESLintの設定で `no-var` を必ず有効にすること。現代において `var` を使う合理的理由はほぼゼロだ。
2. `window` への直接代入は避ける: 「どうしてもグローバルに値を置きたい」という状況は、設計を見直すべきサインだ。もし必要なら、`globalThis` を使うのが現代のスタンダードだ。
- `globalThis` はブラウザでもNode.jsでも、環境を意識せずにグローバルオブジェクトにアクセスできる共通仕様だ。
3. スコープを小さく保つ: 変数は可能な限り関数スコープ、あるいはブロックブロックスコープ(`{}`)内に閉じる。
—
最後に
JavaScriptの仕様は、歴史的な経緯(互換性の維持)とモダンな設計思想が混ざり合った、非常に人間味のある構造をしている。
`var` が `window` に付着するのは、Webというプラットフォームが「動いて当たり前」という過酷な環境で生き抜いてきた名残だ。その泥臭さを理解した上で、あえてクリーンな `const`/`let` を使いこなす。それが、中級から一歩先へ進むエンジニアの矜持だと思うよ。
また何か壁にぶつかったら、いつでも聞きに来るといい。コードは正直だからね。

コメント