「なぜ `var` はバグの温床なのか?」— 巻き上げ(Hoisting)の正体をエンジニアの視点で解き明かす
現場でコードレビューをしていると、今でもたまに `var` を見かけます。「動くからいいじゃないか」という声も聞こえてきますが、結論から言えば、それは「地雷を埋め込みながら歩いている」のと同義です。
特に中級エンジニアが避けて通れないのが、`var` の挙動の核である「巻き上げ(Hoisting)」の理解。なぜ `undefined` が返ってくるのか、ブラウザのエンジンが裏側で何をしているのか。今日は、その泥臭いメカニズムを紐解いていきましょう。
—
1. 巻き上げの正体:コンパイルフェーズの「名簿作り」
JavaScriptはインタプリタ言語ですが、実行前に「コンパイル(解析)」のフェーズが存在します。この時、JavaScriptエンジンはスコープ内をスキャンし、`var` で宣言された変数を見つけると、即座にそのスコープのトップへと「宣言」だけを先送りします。
これが「巻き上げ」です。
重要なのは、「宣言だけが先送りされ、代入は元の場所で行われる」という点です。これが、初期化前にアクセスすると `undefined` になる理由のすべてです。
console.log(myHero); // まだ値は入っていないが、宣言は認識されている。結果は undefined
var myHero = “Spider-Man”;
console.log(myHero); // ここで初めて “Spider-Man” が代入される
/
- 【裏側で起きていること】
- 1. コンパイルフェーズ: var myHero; がスコープ先頭に移動。メモリ確保、値は undefined
- 2. 実行フェーズ: 1行目で undefined を出力、3行目で代入完了
/
2. なぜこれが実務で「悪」なのか
もしこれが関数スコープ内であれば、予期せぬ `undefined` が後続のロジックを破壊します。特に条件分岐の中で `var` を使うと、さらに悲惨なことになります。
function getRole(isAdmin) {
if (isAdmin) {
var role = “admin”;
}
// isAdmin が false でも、宣言だけは巻き上げられているため role は undefined として存在する
console.log(role);
}
getRole(false); // エラーにはならず、undefined が出力される。バグの温床!
`var` には「ブロックスコープ(`{ }` で囲まれた範囲)」という概念が存在しません。そのため、`if` 文や `for` 文の中で宣言しても、その関数全体(あるいはグローバル)に変数が見えてしまうのです。これが現代的な開発において `var` を絶滅させるべき最大の理由です。
3. `let` と `const` がもたらした「健全な世界」
ES6(ES2015)で登場した `let` と `const` は、この巻き上げ問題を根本から解決しました。これらも実は巻き上げ自体は行われていますが、「一時的死滅領域(Temporal Dead Zone: TDZ)」という概念が導入されています。
// console.log(myVillain); // ここでアクセスすると ReferenceError が発生する
let myVillain = “Green Goblin”;
`let` や `const` は、宣言の行に到達するまでは変数へのアクセスを物理的に遮断します。これにより、「値が入っていないのに使えてしまう」という JavaScript の悪癖を、言語仕様レベルで防ぐことができるようになったのです。
—
現場で役立つ「モダンな書き方」の指針
チームでコードを書くとき、僕が後輩に伝えている「変数宣言の鉄則」を共有します。
1. `var` は完全に禁止する: ESLintの設定で `no-var` をルール化し、CIで弾くのが当たり前です。
2. 基本は `const`: 再代入が必要ないものはすべて `const` にします。意図しない値の書き換えを防ぐことが、バグを減らす最短距離です。
3. 必要な時だけ `let`: ループのカウンターなど、再代入が不可欠な場合のみ `let` を許可します。
まとめ:技術の負債を積み上げないために
JavaScript の歴史は、この「巻き上げ」のような仕様の「負の遺産」との戦いでした。`var` を使い続けることは、古い仕様の挙動を脳内に保持し続けるという高いコストを払い続けることです。
もし今、既存のコードベースで `var` を見かけたら、それは「リファクタリングのチャンス」だと捉えてください。`let` や `const` に置き換えるだけで、コードの堅牢性は劇的に向上します。
技術は常にアップデートされています。過去の仕様に引きずられず、今の時代に合った「クリーンで読みやすいコード」を一緒に追求していきましょう。次回のレビューで、皆さんのコードから `var` が消えていることを期待しています。

コメント