こんにちは!フロントエンドからNode.jsの深部まで、JavaScriptの生態系を知り尽くしたチーフアーキテクトの私です。
皆さんは日々の開発で、ESLintなどの静的解析ツールから「`var` を使わないで! `let` や `const` を使いましょう」と怒られた経験はありませんか? 「動くんだからどっちでもいいじゃないか」と思ってしまいがちですが、実はここにJavaScriptという言語の歴史、そしてV8エンジンなどのランタイムが抱えるメモリ管理の本質が隠されているのです。
今回は、なぜモダンな開発において `var` が厳禁とされ、静的解析ツールが目を光らせているのか。その技術的根拠を、スコープやメモリの裏側まで踏み込んで分かりやすく紐解いていきますね。ここをクリアすれば、あなたもJavaScriptの挙動を完全に支配するワンランク上のエンジニアになれますよ!
—
1. なぜ `var` は悪者扱いされるのか?(基本のおさらい)
まずは、`var` と、私たちが普段何気なく使っている `let` / `const` の決定的な違いからおさらいしましょう。
JavaScriptが生まれた初期の頃、変数を宣言するためのキーワードは `var` しかありませんでした。この `var` には、「関数スコープ」という性質があります。つまり、関数の中で宣言された変数以外は、すべてプログラム全体の共有スペースである「グローバルスコープ」に飛び出してしまいます。
図解:スコープのイメージ
【 var の世界(関数スコープ)】
if (true) {
var badVariable = “私はどこからでも見えちゃいます”;
}
console.log(badVariable); // => えっ、ブロックの外なのに読めちゃう!?(汚染発生)
【 let / const の世界(ブロックレベルスコープ)】
if (true) {
let safeVariable = “私はこのブロックの中だけの秘密です”;
}
console.log(safeVariable); // => ReferenceError! ブロックの外からはアクセス不可(安全)
`let` や `const` が登場したことで、私たちは `if` 文や `for` 文などの小さなブロック単位(`{}` の中)で変数を閉じ込められるようになりました(これをブロックレベルスコープと言います)。しかし、`var` にはその防壁が通用しません。
—
2. スコープチェーンの探索コストと「名前衝突」の恐怖
大規模なアプリケーション開発を想像してみてください。世界中のオープンソースライブラリを組み込み、何十人ものエンジニアが一つのコードベースを触るとき、`var` を使っていると何が起きるでしょうか。
予期せぬ「上書き(名前衝突)」
グローバルスコープ(あるいは関数スコープの最上位)に置かれた変数や関数は、どこからでも書き換え可能です。
// どこかのライブラリが読み込まれる
var currentUser = “Admin”;
// あなたが別のファイルでうっかり同じ名前で宣言してしまう
var currentUser = “Guest”;
// 最初に動いていた管理画面の処理がバグる!
JavaScriptのランタイムは、変数が参照されたとき、現在のスコープから外側に向かって順番に変数を探しに行きます。これを「スコープチェーンの探索」と呼びます。
グローバル空間に汚染された変数があふれ返っていると、V8エンジンは変数を解決するたびに余計な探索コストを支払い続けます。さらに、どこで値が書き換わったのか追跡不可能な「スパゲッティコード」が完成してしまうのです。
—
3. 巻き上げ(Hoisting)の罠
`var` を語る上で避けて通れないのが「巻き上げ(Hoisting)」です。
JavaScriptのコードは、実行される前にエンジンによって一度「コンパイル(解析)」されます。その際、`var` で宣言された変数は、「宣言だけがスコープの最上部に持ち上げられ、初期値として `undefined` が代入される」という奇怪な挙動をします。
console.log(secretCode); // エラーにならない! “undefined” が出力される
var secretCode = “TOP_SECRET_123”;
console.log(secretCode); // “TOP_SECRET_123” が出力される
初学者の頃は「エラーにならずに動いたからラッキー」と思いがちですが、これは大規模開発においては最悪の挙動です。存在しないはずの変数が `undefined` という中途半端な状態で存在し、後から予期せぬタイミングで値が代入されるため、バグの温床になります。
これに対し、`let` や `const` も巻き上げ自体は行われますが、こちらは初期化が行われる前にアクセスすると「Temporal Dead Zone(一時的死空間)」というエラー(ReferenceError)を発生させ、強制的にクラッシュしてくれます。
「おっと、変数が定義される前に使おうとしてますよ!」とエンジンが教えてくれるため、バグを未然に防げるのです。
—
4. 静的解析ツール(ESLint)が `var` を検知する仕組み
さて、ここからが本題です。なぜESLintなどの静的解析ツールは、コードを実行する前段階(ビルド時やエディタ上)で `var` を血眼になって検知し、警告(エラー)を出すのでしょうか?
それは、「ランタイム(実行時)にV8エンジンやブラウザへ無駄な負荷をかけず、メモリの最適化を担保するため」です。
AST(抽象構文木:Abstract Syntax Tree)という仕組みを耳にしたことはあるでしょうか? 静的解析ツールは、私たちの書いたJavaScriptコードを解析し、ツリー構造に変換します。
[Program]
└── [VariableDeclaration (kind: “var”)] <-- ESLintのパーサーがここを瞬時に検知!
└── [Identifier (name: "badVariable")]
ESLintのルール(例: `no-var`)は、このASTの中に `kind: "var"` のノードを発見した瞬間、「危険な構文が検知されました」と赤文字で警告を飛ばします。
V8エンジンのメモリ空間(ヒープ)の視点
モダンなJavaScriptエンジン(V8など)は、変数がどこで使われ、いつ不要になるのか(ライフサイクル)を厳密に解析し、メモリ(ヒープ領域)を効率的に割り当てようとします。
`var` によるグローバル汚染や関数スコープの肥大化は、「不要になったはずのデータがいつまでもメモリ上に残り続ける(メモリリークの原因)」状態を作り出します。
逆に、`let` や `const` を使い、ブロックスコープ内で適切に変数を閉じ込めれば、そのブロックの実行が終了した瞬間にエンジンはメモリを回収(ガベージコレクション)しやすくなります。
つまり、静的解析ツールが `var` を弾くのは、単なる「古い書き方だからダサい」という理由ではなく、「アプリケーションのメモリ効率を最適化し、予期せぬバグをコンパイル(解析)段階で根絶するため」の極めて合理的なエンジニアリングなのです。
—
まとめ:今日からの実践
いかがでしたでしょうか? `var`、そしてグローバルスコープ汚染の恐ろしさが、単なる文法の好き嫌いではなく、ランタイムの挙動やメモリ管理に直結していることがお分かりいただけたかと思います。
- 変数を宣言するときは、基本は `const`、再代入が必要な場合のみ `let` を使う。
- `var` は絶対に書かない(ESLintに任せず、自分自身の意識から排除する)。
たったこれだけのルールを守るだけで、あなたの書くJavaScriptコードの信頼性は劇的に向上します。
ここをクリアすれば、JavaScriptの基本はバッチリマスターできたも同然です!
さあ、今日のコーディングから `var` を一切排除し、モダンで堅牢なコードを書き上げていきましょう。それでは、また次のアーキテクチャ解説でお会いしましょう!