こんにちは!フロントエンドからNode.jsの深層まで、日夜コードと向き合っているシニアアーキテクトの私です。
JavaScriptを学び始めると、変数を宣言するときに `let` や `const` を使いますよね。「あれ、昔のコードやうっかりミスで `x = 10` みたいに `var` すら書かずに値を代入しちゃっても動くことあるけど、何が違うの?」と思ったことはありませんか?
実は、この「変数の宣言をサボる(暗黙的宣言)」という行為は、JavaScriptのエンジン(Google ChromeやNode.jsで使われている V8 など)にとって最悪の悪夢なんです。
今回は、なぜ `use strict`(厳格モード)を使って暗黙のグローバル変数を絶対に排除しなければならないのか、V8エンジンの「裏側の思考」を覗き見しながら、分かりやすく紐解いていきましょう。ここをクリアすれば、あなたの書くコードのパフォーマンスも品質も、一段も二段もレベルアップしますよ!
—
1. 「暗黙的グローバル変数」の正体と、その恐ろしい挙動
まずは、私たちがやってしまいがちな「うっかりミス」のコードを見てみましょう。
// あえて ‘use strict’ を使っていない状態を想定してください
function calculateTotal(price, tax) {
// 宣言キーワード (let, const, var) を書き忘れた!
total = price (1 + tax);
return total;
}
console.log(calculateTotal(1000, 0.1)); // 1100 が表示されます
console.log(total); // えっ!? 関数の外なのに total が参照できてしまう!
一見すると、「関数の外からでも結果が取れて便利じゃん」なんて思ってしまうかもしれませんが、これはJavaScriptにおける最大のバグの温床です。
`let` や `const` を付けずに値を代入すると、JavaScriptエンジンは「おっ、この名前の変数がどこにも見当たらないな? よし、じゃあ大元(グローバルオブジェクト、ブラウザなら `window`)のプロパティとして勝手に作って登録してやるか!」と解釈します。これが暗黙のグローバル変数(Implied Global)です。
スペルミスをしたときなども、エラーを出さずにグローバル空間を汚染してしまうため、大規模なアプリケーションではどこで変数が書き換わったのか分からなくなる魔物と化します。
—
2. なぜV8エンジンは「暗黙的グローバル変数」を嫌うのか?(最適化の壁)
ここからが本題、V8エンジン(JavaScriptランタイム)の内部の話です。
JavaScriptは一見すると動的で「何でもあり」な言語に見えますが、現代のブラウザやNode.jsは、実行速度を極限まで高めるためにJIT(Just-In-Time)コンパイラという仕組みを使って、コードをマシーン語(C++やアセンブリに近い超高速なコード)に翻訳しています。
V8がコードを爆速に最適化するとき、最も重要視するのが「スコープの静的解決(Static Scoping)」です。
図解:V8の脳内イメージ
[まともなコード (let / const)]
関数スコープ内で変数が完結
└─ V8: 「この変数はこの関数の中だけで使うんだな。レジスタに直接割り当てちゃおう!」 (⚡超高速)
[暗黙的グローバル変数]
変数 `total` がどこから来たか分からない
└─ V8: 「あれ? この `total` って、グローバルオブジェクトのプロパティ? もしかしたら途中で別のスクリプトに書き換えられたかもしれないぞ……」
└─ V8: 「安全のために、毎回グローバルオブジェクトのメモリマップをハッシュ迷路のようにルックアップ(検索)しなきゃ……」 (🐢激遅)
V8のコンパイラは、変数が「どこに存在し、型はどう固定されているか」を事前にコンパイル時に確定させたいのです。
しかし、暗黙的グローバル変数は、「グローバルオブジェクト(Windowやglobal)のプロパティへの動的な動的プロパティアクセス」にすり替わります。オブジェクトのプロパティはいつ誰に書き換えられるか分からない(プロトタイプ汚染などのリスクもある)ため、V8は最適化の仮説(Inline Cachingなど)をすべて捨てざるを得なくなり、コードの実行速度がガクンと落ちてしまうのです。
—
3. 救世主 `use strict`(厳格モード)の登場
こうしたV8の悲鳴を止め、私たち開発者にも安全なコードを書かせるために用意されたのが、厳格モード(Strict Mode)です。
ファイルの先頭、あるいは関数の先頭に `”use strict”;` と書くだけで、JavaScriptエンジンに対して「甘えを一切許さない厳格なルールで動いてね」と指示を出すことができます。
“use strict”; // ← ここがポイント!
function calculateTotal(price, tax) {
// 宣言キーワードを忘れてみる
total = price (1 + tax);
return total;
}
// 実行すると…
// Uncaught ReferenceError: total is not defined
`”use strict”;` を宣言している状態で暗黙的グローバル変数を作ろうとすると、JavaScriptエンジンは即座に `ReferenceError`(参照エラー) を吐いてコードの実行を止めてくれます。
「エラーで止まるなんて怖い」と思いましたか?
いいえ、とんでもない! 「実行時におかしな挙動をする(サイレントバグ)」よりも、「開発時に即座にエラーで教えてくれる」方が100倍ありがたいのです。なぜなら、V8エンジンはエラーで立ち止まってくれるおかげで、「お、ここでは変数が確実に宣言されているな」と安心してコードをガリガリに最適化(機械語へのコンパイル)できるからです。
—
4. モダンな開発における `use strict` の扱いとベストプラクティス
「でも、最近のモダンな開発やES Modules(`import` / `export` を使うファイル)では、わざわざ `”use strict”;` って書かないよ?」と気づいた方は鋭いですね。
その通りです。
現代のJavaScript環境(ES Modules、TypeScript、BabelやWebpackなどのトランスパイラを通す環境)では、デフォルトで厳格モードが強制(Strict Mode by default)されています。私たちが意識せずとも、モダンなJSファイルはすべて厳格モードの安全な保護下で動き、V8にとって最高の最適化しやすい状態で実行されているのです。
それでもなお、レガシーなスクリプトファイルを書く際や、Node.jsのCommonJS環境(`require`を使う古いスタイル)でファイルを直接動かす際には、このおまじないを忘れないことがプロとしてのたしなみになります。
今日のまとめ
1. 暗黙的グローバル変数(`x = 10` のような宣言なし代入)は、V8エンジンの静的解析と最適化を破壊する。
2. V8は変数のありかを追えなくなり、毎回重いプロパティルックアップを行うためパフォーマンスが低下する。
3. `use strict`(またはES Modules)を使うことで、こうした甘えをコンパイル段階(または実行初期)でエラーとして弾き、V8に最高のパフォーマンスを発揮させることができる。
「変数宣言をちゃんとする」という基本中の基本のルールは、単にコードを綺麗に保つためだけではなく、裏側で動くV8エンジンへの思いやり(最高速を引き出すためのパスポート)だったんですね。
ここをクリアすれば、もうあなたの書くコードはV8にとっても愛されるエリートコードです。自信を持ってモダンなJSライフを楽しんでいきましょう!