【実務・中級編】厳格モード(use strict)が変数の暗黙的宣言を許さない技術的理由:最適化の観点から – JavaScriptコア文法・モダン言語仕様と変数・関数解析バイブル

厳格モード(`”use strict”`)とV8の深淵:暗黙のグローバル変数がコンパイラの最適化を殺す理由

コードレビューの現場で、未だに `var` が放置されていたり、スコープの意図が曖昧なコードを見かけることがある。そして、ファイルやモジュールの先頭に `”use strict”;` を書き忘れているケースも珍しくない。

「動いているからいいだろう」という安易な妥協は、V8エンジン(あるいはSpiderMonkeyやJavaScriptCore)の心臓部であるコンパイラパイプラインにおいて、静的な最適化の機会を完全に粉砕している。

今回は、フロントエンドからNode.jsまで、現代のWebエンジニアが知るべき「なぜ暗黙的宣言が悪なのか」「厳格モードがなぜコンパイラの性能を引き出すのか」を、V8のランタイム挙動とメモリアーキテクチャの観点から徹底的に解剖する。

—

1. 暗黙的グローバル変数(Implied Globals)がV8の静的解析を殺す理由

JavaScriptには、かつて「変数宣言を忘れても、代入した瞬間にグローバルオブジェクト(ブラウザなら `window`、Node.jsなら `global`)のプロパティとして勝手に生えてくる」という悪名高い仕様が存在した。

function calculateTotal(price, tax) {
// 宣言キーワード(const / let / var)が抜けている!
total = price (1 + tax);
return total;
}

この `total` こが、暗黙的グローバル変数(Implied Global)である。一見すると便利に見えるかもしれないが、これがV8エンジンのJIT(Just-In-Time)コンパイラにとって「最悪の毒」となる。

動的プロパティ lookup の地獄

V8の核心は、オブジェクトのプロパティアクセスを高速化する「Hidden Class(隠しクラス / Shapes)」と、そこから派生する「Inline Caching(IC)」メカニズムにある。

あるオブジェクトのプロパティ構造が固定されていれば、V8はメモリ上のオフセット(何番地のメモリか)を直接指すことで、高速なプロパティアクセスを実現する。

しかし、暗黙的グローバル変数への代入は、実質的にグローバルオブジェクトに対して動的に新しいプロパティを追加する操作に他ならない。
グローバルオブジェクトはアプリケーションのライフサイクル全体を通じて膨大な数の変数が動的に出入りするため、V8はこれを「構造が予測不能な辞書型オブジェクト(Dictionary Mode)」として扱わざるを得なくなる。

1. オフセットのハードコードが不可能になる:
コードのどこからでも書き換え可能なグローバルスコープに、暗黙的に変数が追加されると、コンパイル時に変数のメモリアドレスを確定できなくなる。
2. インラインキャッシュ(IC)のミス連発:
プロパティの参照・代入が「辞書引き(ハッシュマップのキー検索)」に格下げされ、CPUキャッシュのヒット率が劇的に低下する。

結果として、JITコンパイラはコードを最適化された機械語にコンパイルできず、低速なインタプリタ実行や、非効率なメガモーフィック(Megamorphic)な状態へとフォールバックする。

—

2. 厳格モード(`”use strict”`)がもたらすコンパイル時の規律

`”use strict”;` を宣言すると、JavaScriptエンジンはパース(構文解析)の段階で明確なエラーを吐くようになる。

“use strict”;

function calculateTotal(price, tax) {
total = price (1 + tax); // ReferenceError: total is not defined
return total;
}

この「実行前にエラーで弾く」という挙動は、開発者のミスを防ぐだけでなく、コンパイラに対して「このスコープ内では、すべての変数がレキシカル(静的)に解決される」という強力な保証を与える。

スコープの静的解決(Lexical Scoping)と最適化

変数が `const` や `let` によって適切に宣言されている場合、V8のパーサー(Ignition)は、その変数がどのスコープに属し、どのバイトコードから参照されるかを完全に把握できる。

  • レジスタ割り当ての最適化:

静的にスコープが確定した変数は、ヒープ上のオブジェクトのプロパティではなく、CPUのレジスタやスタックフレーム上に直接アロケートされる。これにより、メモリアクセスのオーバーヘッドが極限まで削ぎ落とされる。

  • デッドコードの排除(Dead Code Elimination):

使われていない変数や到達不可能なコードブロックを、トランスパイラやJITコンパイラが安全に消去(Uglify / Tree Shaking)できるようになる。

—

3. 実務で差がつく:堅牢で最適化しやすいコード設計パターン

テクニカルリードとしてコードレビューを行う際、私はチームメンバーに「ただ動くコード」ではなく、「コンパイラが恋に落ちるコード」を書くことを求めている。

以下に、厳格モードを前提とし、V8のヒープ負荷を最小限に抑えたモダンな設計・実装パターンを示す。

パターン:不変性(Immutability)を担保したデータ処理パイプライン

DOM操作や非同期API(Fetch APIなど)から取得した巨大なJSONデータを扱う際、ミュータブルな変数を乱用すると、V8のガベージコレクタ(GC)に過剰な負荷がかかる。以下のコードは、厳格モード下でスコープを完全に隔離し、V8の最適化を最大限に引き出す実用的なパターンだ。

// モジュールやIIFE、あるいは現代のESM環境では暗黙的にstrict mode
“use strict”;

/