【実務・中級編】厳格モード(use strict)が変数の宣言に与える強制力 – JavaScriptコア文法・モダン言語仕様と変数・関数解析バイブル

【コードレビュー】暗黙のグローバル変数はなぜ悪か?『use strict』がV8とプロダクションコードを守る理由

テックリードの私だ。今日のコードレビューで、また若手が書いたコードにこんな記述を見つけた。

function updateUserProfile(userId, data) {
usreProfile = data; // ああ、タイポだ
// …
}

「動くからいいか」ではない。この数文字のタイポが、のちに本番環境で何万人ものユーザーを巻き込むメモリリークや、原因不明の状態競合(Race Condition)を引き起こす引き金になるのだ。

今回は、JavaScriptの原点にして極限の守護神である 厳格モード(`’use strict’;`) が、変数の宣言とスコープ、そしてV8エンジンのメモリ空間にどのような強制力を与えるのか。そのメカニズムと実務での設計パターンをロジカルに解説しよう。

—

1. 厳格モードとは何か? V8エンジンとスコープの深層

JavaScriptは、その誕生初期の歴史的背景から「寛容すぎる言語」として設計された。変数を宣言し忘れて値を代入しても、エラーを吐くどころか、エンジン側が勝手にグローバルオブジェクト(ブラウザなら `window`、Node.jsなら `global`)のプロパティとしてその変数を生み出してしまう。これが「暗黙のグローバル変数(Implied Globals)」の正体だ。

暗黙のグローバル変数がV8ヒープに与える罪

V8エンジンなどのモダンなJavaScriptエンジンは、スコープチェーンを最適化し、変数を高速にアクセスするために隠しクラス(Hidden Classes / Shapes)やインラインキャッシュを活用している。

しかし、宣言なしに代入された識別子は、静的スコープ解析(Lexical Scoping)の対象外となり、グローバルオブジェクトの動的なプロパティとして扱われる。これは以下の弊害を生む。

1. メモリリークの温床: グローバルオブジェクトに紐づいた変数は、アプリケーションが破棄されるまでガベージコレクション(GC)の対象から外れ、メモリを圧迫し続ける。
2. スコープ汚染と衝突: 別のモジュールやサードパーティ製ライブラリが同じ名前のグローバル変数を使用していた場合、意図しない上書き(汚染)が発生する。
3. 静的解析の無効化: WebpackやViteなどのバンドラ、TypeScriptコンパイラ、ESLintなどのLinterが早期にエラーを検知できなくなる。

ここで `’use strict’;`(厳格モード)を宣言すると、JavaScriptパーサーとV8ランタイムは「曖昧なコードを一切許容しない安全なモード」へと切り替わる。未宣言の変数への代入は、実行時(あるいはパース時)に即座に `ReferenceError` をスローし、バグを未然にシャットアウトするのだ。

—

2. 実務で直面するバグと「堅牢な設計パターン」

では、実際のフロントエンド開発やコンポーネント設計において、厳格モードがどのようにコードの品質を担保するのか見ていこう。

悪い例:暗黙のグローバルが引き起こす状態管理の崩壊

以下のコードを見てほしい。ESモジュール(`type=”module”`)やモジュールバンドラを使用していればデフォルトで strict モジュールとして動作するが、レガシーなスクリプトタグやIIFE(即時実行関数)環境では、依然として事故が起きる。

// 【アンチパターン】厳格モードがない、またはスコープ汚染を起こすコード
function initializeCart() {
// let や const を書き忘れた、あるいはタイポした
itemCount = 0;

window.addEventListener(‘click’, () => {
itemCount++; // グローバル空間の itemCount を参照・変更し続ける
console.log(itemCount);
});
}

initializeCart();
// window.itemCount が勝手に生成され、他のスクリプトからもアクセス・変更可能になる

このコードの恐ろしいところは、エラーを出さずにサイレントに動作し、アプリが複雑化するにつれて「どこから書き換わっているのか分からない状態」を作り出す点である。

良い例:厳格モードとイミュータブルな設計による堅牢化

プロダクションコードでは、ファイル単位、あるいは関数単位で厳格モードを徹底し、変数は常に `const`(必要に応じて `let`)でスコープを厳格に閉じ込めるべきだ。

/

  • @fileoverview カートの状態管理モジュール
  • @-strict-mode enabled

/
‘use strict’;

/

  • カートの初期化とイベントバインドを行う堅牢なファクトリー関数
  • @param {HTMLElement} triggerElement – クリックトリガーとなるDOM
  • @returns {Object} カートの操作インターフェース

/
export function createCartManager(triggerElement) {
// スコープ内に完全に閉じたプライベート変数(暗黙のグローバルは発生しない)
let itemCount = 0;

const handleIncrement = () => {
itemCount++;
return itemCount;
};

// DOMイベントの登録
triggerElement.addEventListener(‘click’, handleIncrement, { passive: true });

// 外部へは必要最小限のインターフェースのみを公開(カプセル化)
return {
getItemCount: () => itemCount,
reset: () => {
itemCount = 0;
}
};
}

—

3. DOM操作・配列処理におけるパフォーマンスと厳格モードの恩恵

テクニカルリードとして、DOM操作や膨大な配列データを扱う際にも、厳格モードの恩恵と変数の適切な宣言がパフォーマンスにどう影響するか言及しておこう。

1. `this` のバインディング最適化とメモリ効率

厳格モードでは、関数呼び出し時の `this` のデフォルト値が `undefined` になる(非厳格モードではグローバルオブジェクト)。
これにより、意図しないグローバルオブジェクトへのバインディングを防ぎ、V8が関数を最適化(JITコンパイル)する際の型推論のブレをなくすことができる。

2. 大規模配列の処理におけるメモリ効率

現代のWebアプリでは、数万件のAPIレスポンス(配列データ)を `Array.prototype.map()` や `reduce()` で処理することが日常茶飯事だ。

‘use strict’;

/