こんにちは!フロントエンドからNode.jsの深部まで、JavaScriptの生態系をくまなく見つめてきたチーフアーキテクトの私です。
皆さんは日々のコーディングで `use strict`(厳格モード)を意識していますか?「なんとなくおまじないとして書いている」「Linterが怒るから入れている」という方も多いかもしれませんね。
しかし、この小さな一行の有無が、JavaScriptという言語の挙動を根底から変えてしまうとしたらどうでしょう?特に「変数の宣言」において、`use strict` は私たちのコードを予期せぬバグから守る最強の盾となります。
ここをクリアすれば、JavaScriptの変数スコープとランタイムの挙動に関する基本はバッチリマスターできますよ。さあ、その知見の扉を開いていきましょう。
—
1. そもそも「変数の宣言漏れ」で何が起きていたのか?
JavaScriptを学び始めたばかりの頃、うっかり `let` や `const`、あるいは古いコードの `var` を書き忘れて、次のように変数に値を代入してしまったことはありませんか?
function calculateTotal() {
// 宣言キーワード(let/const)を書き忘れた!
total = 1000;
return total 1.1;
}
calculateTotal();
console.log(total); // 1000 と出力されてしまう…あれ?
他の厳格なプログラミング言語(JavaやC#など)であれば、ここで「変数 `total` が宣言されていません」とコンパイルエラーになりますよね。
しかし、従来のJavaScript(非厳格モード)は非常に寛容(すぎる)でした。なんと、このコードを実行すると、エラーになるどころか、勝手にグローバルオブジェクト(ブラウザなら `window`、Node.jsなら `global`)のプロパティとして `total` を新しく作り出してしまうのです。
これが、悪名高い「暗黙のグローバル変数(Implied Global)」の生成です。
恐ろしいバグの温床:タイポが生む悲劇
この仕様がどれほど危険か、想像がつくでしょうか。例えば、次のようなケースです。
let userCount = 10;
function updateCount() {
// 本来は userCount を書き換えたいのに、うっかり ‘c’ を抜かしてタイポした!
userCout = 20;
}
updateCount();
console.log(userCount); // 10 (あれ?変わってない)
console.log(global.userCout); // 20 (なんと、意図せずグローバル変数が生えてしまった!)
プログラムが巨大化し、ファイルの分割やモジュール化が進む現代において、意図しないグローバル変数が勝手に生成されるのは、デバッグ不可能なバグの温床になります。どこからでも書き換え可能な変数がグローバル空間にばらまかれ、変数名の衝突(ネームスペースの汚染)を引き起こすからです。
—
2. 救世主 `use strict`(厳格モード)の登場
こうしたJavaScriptの「緩すぎる歴史的負債」を断ち切り、より堅牢で予測可能なコードを書くためにECMAScript 5で導入されたのが `use strict`(厳格モード) です。
使い方は極めてシンプル。ファイルや関数の先頭に文字列として記述するだけです。
“use strict”;
function secureCalculate() {
// 変数の宣言漏れがあると……?
strictTotal = 1000; // 💥 ここで即座にエラー発生!
return strictTotal 1.1;
}
secureCalculate();
このコードを実行すると、V8などのJavaScriptエンジンは次のようなReferenceError(参照エラー)を投げます。
ReferenceError: strictTotal is not defined
「おっと、君は `let` や `const` で変数を作っていないのに、いきなり値を代入しようとしているね? そんな危険なことはさせないよ!」と、ランタイムが厳格にストップをかけてくれるのです。
—
3. なぜ `use strict` はエラーにできるのか?(言語仕様とランタイムの裏側)
ここから少しだけ、JavaScriptエンジン(V8など)の内部で何が起きているのか、エンジニアの視点で少し深く覗いてみましょう。
JavaScriptの仕様(ECMAScript Specification)において、コードの実行は「Strict Mode Code」と「Non-strict Mode Code」に厳密に分類されます。
1. 非厳格モードの場合:
識別子への代入(Assignment Expression)に遭遇した際、現在のスコープチェーンを遡ってもその変数がどこにも見つからない場合、JavaScriptエンジンは「ああ、グローバルオブジェクトにその名前のプロパティを追加したいんだな」と親切心(お節介)から解釈し、グローバルオブジェクト上に暗黙的にプロパティを動的生成します。
2. 厳格モード(`use strict`)の場合:
識別子の解決に失敗した瞬間、エンジンはその親切心を完全に封印します。仕様書に定められたルールに従い、「未宣言の識別子への代入は、即座に ReferenceError をスローしなければならない」という鉄の掟を執行するのです。
このチェックはランタイムのパース・実行フェーズの極めて初期に行われるため、タイポによるバグが本番環境のユーザーのブラウザ上で沈黙したまま実行されるのを未然に防ぎます。
—
4. 現代のJavaScript開発における `use strict`
「でも、最近は Babel や TypeScript、あるいは Vite のようなモダンなバンドラを使っているから、自分であえて `”use strict”;` なんて書いたことないよ?」という方もいらっしゃるでしょう。
その通りです。現代のフロントエンド開発において、私たちが書くコードは多くの場合、ES Modules(ESM)として扱われたり、ビルドツールによって自動的にモジュールスコープに変換されたりします。
実は、ES Modulesの内部や、クラス(Class)の構文の内部では、明示的に `”use strict”;` と書かなくても、自動的に厳格モードが適用されるという仕様になっています。
しかし、だからといって「知らなくていい知識」ではありません。
レガシーなスクリプトファイルに手を加える時や、Node.jsのプレーンなCommonJS環境(`require`を使う環境)でスクリプトを書く際、あるいは独自の小さなユーティリティ関数をサクッと書く際には、自ら `”use strict”;` を宣言する習慣が身を守る大きな武器になります。
—
まとめ:今日から使えるベストプラクティス
変数の宣言とスコープにおける `use strict` の強制力、その本質がお分かりいただけたでしょうか?
- 暗黙のグローバル変数の生成を防ぐ:タイポによるバグやグローバル汚染を根絶する。
- エラーを早期に検知する:サイレントエラーに悩まされる時間をゼロにし、開発効率を爆上げする。
JavaScriptという言語の足場を固め、より安全で信頼性の高いコードベースを構築するために、変数は必ず `let` や `const` で宣言し、必要に応じて厳格モードの恩恵をしっかりと受けていきましょう。
ここをクリアしたあなたなら、もう変数周りの挙動で迷うことはありません。自信を持って、次のステップへ進んでください!