グローバル汚染とは何か
グローバル汚染は「意図せずグローバルスコープ(window/globalThis)に変数や関数が増えてしまい、別コードと衝突や上書きが起きる」状態です。衝突は“同じ名前”が複数ファイルで使われたときに発生し、上書きは“後から読み込んだスクリプト”が前の値を変えてしまうと起きます。ここが重要です:グローバルは共有資産。小さなミスが全体のバグに直結します。ES6+ の let/const とモジュール化は、この問題を根本から減らすために設計されています。
// 悪い例:無防備なグローバル
rate = 0.1; // 暗黙のグローバル(宣言なし代入)
function calc() {} // グローバル関数
JavaScriptvar と let/const のグローバル挙動差
ブラウザでは、var のグローバル宣言は window のプロパティになります。つまり誰でも触れてしまい、delete も効かないことがあります。対して let/const のグローバル宣言は window に現れず、“環境レコード”内の束縛として扱われます。ここが重要です:グローバルをどうしても使う場合、var ではなく let/const を選ぶだけで汚染リスクが下がります。
var gv = 1;
let gl = 2;
const gc = 3;
console.log(window.gv); // 1(ぶら下がる)
console.log(window.gl); // undefined(ぶら下がらない)
console.log(window.gc); // undefined(ぶら下がらない)
JavaScript宣言なし代入(strict mode でない場合)は暗黙のグローバルを作る最悪手です。必ず let/const/var で宣言してから使います。
"use strict";
// x = 1; // ReferenceError(暗黙のグローバルを防ぐ)
let x = 1;
JavaScriptES Modules(import/export)で“ファイルごとにスコープ”を作る
ES Modules はファイル単位で独立スコープを作り、トップレベルの宣言がグローバルへ漏れません。ここが重要です:グローバル汚染を構造的に避ける最良の方法は「各ファイルをモジュールにする」ことです。必要な名前だけ export/import し、外へ“公開するインターフェース”を限定します。
// math.js(モジュール)
export const TAX_RATE = 0.1;
export function priceWithTax(p) { return Math.round(p * (1 + TAX_RATE)); }
// app.js(モジュール)
import { priceWithTax } from "./math.js";
console.log(priceWithTax(100));
JavaScriptモジュールのトップレベル変数は他ファイルから直接見えません。公開するものを選べるため、名前衝突が激減します。ブラウザでは <script type=”module”> を使うだけで有効になります。
<script type="module" src="app.js"></script>
HTML名前空間オブジェクトに“ひとまとめ”にする(必要な場合だけ)
古い環境やモジュールが使えない状況では、“単一のグローバルを定義して、その下にまとめる”名前空間パターンが安全です。ここが重要です:グローバルを1つに限定し、内部はプロパティとして整理することで衝突範囲を最小化できます。Object.freeze で外部からの誤更新を抑えるのも有効です。
// 1つだけ作るグローバル
const App = Object.freeze({
math: {
TAX_RATE: 0.1,
priceWithTax(p) { return Math.round(p * (1 + this.TAX_RATE)); },
},
utils: {
trimLower(s) { return String(s).trim().toLowerCase(); }
}
});
// 使用側
const total = App.math.priceWithTax(100);
JavaScriptIIFE とブロックスコープで“閉じる”(レガシー対策)
ES6+ モジュールが使えない場合、IIFE(即時実行関数)や単純なブロックでスコープを閉じ込めます。ここが重要です:ファイル内の一時変数やヘルパーを外へ漏らさないだけでも、汚染は大きく減ります。let/const のブロックスコープと組み合わせれば、事故をさらに防げます。
// IIFE(古典的モジュールパターン)
(function () {
const rate = 0.1;
function priceWithTax(p) { return Math.round(p * (1 + rate)); }
// ここにファイル内の処理を書く
}());
// 外から rate / priceWithTax は見えない
// 単純なブロックでも OK(ES6+)
{
const key = query.trim().toLowerCase();
result = items.filter(x => x.name.toLowerCase().includes(key));
} // key はこのブロック限り
JavaScript実務での指針と例題(重要ポイントの深掘り)
まず const、必要なら let。宣言なし代入は厳禁。トップレベルは“モジュールの中”で宣言し、グローバルへ公開しないのが基本です。ここが重要です:公開は export/import に限定し、APIの入り口を絞ると、差し替え・テスト・保守が容易になります。
// userRepo.js(モジュール内で閉じる)
const cache = new Map(); // 外へ漏らさない内部状態
export async function getUser(id) {
if (cache.has(id)) return cache.get(id);
const user = await fetch(`/api/users/${id}`).then(r => r.json());
cache.set(id, user);
return user;
}
JavaScriptイベントリスナーやタイマーのクロージャが“大きな配列やDOM”をキャプチャして長生きすると、半ばグローバル同然の重い参照になります。必要最小限だけ渡し、不要になったら解除します。名前空間下に“dispose”関数を用意するのも有効です。
const listeners = [];
function attach(el) {
const h = () => console.log(el.id);
el.addEventListener("click", h);
listeners.push(() => el.removeEventListener("click", h));
}
// 終了時に
for (const off of listeners) off();
JavaScriptよくある落とし穴と対策
暗黙のグローバル(宣言なし代入)、var のグローバル(windowへぶら下がる)、トップレベルのユーティリティ乱立、switch や大域スコープでの同名衝突が典型です。ここが重要です:strict mode を常用し、type=”module” を使い、ファイル内の実装詳細はブロック/IIFEに閉じる。公開面は import/export に限定し、どうしてもグローバルが必要なら“1つの名前空間”に集約します。
"use strict";
JavaScriptまとめ
グローバル汚染回避の核心は「スコープを意図的に小さくし、公開面を限定する」ことです。let/const は window へぶら下がらず、TDZ で宣言前使用も防ぐため、最初の防波堤になります。最も強力なのは ES Modules:ファイルごとに独立スコープを作り、必要な名前だけ export/import する。古い環境では IIFE やブロックスコープで閉じ、やむを得ないグローバルは名前空間オブジェクトに一元化する。この方針を徹底すれば、初心者でも安全で拡張しやすい ES6+ のコードが書けます。

