概要
JavaScriptのスコープは「変数が見える範囲」を制御する設計上の境界である。
そして var
/ let
/ const
によって、スコープの境界が明確に変化することは、変数設計において本質的な選択ポイントになる。
ES6以前は「関数スコープ」のみだったが、let
と const
の登場により「ブロックスコープ」が導入された。
この変化は変数の責務とライフタイム(有効期間)を制御する設計戦略として極めて重要である。
本稿では、関数スコープとブロックスコープの違い、それぞれの使い分け、設計的判断軸を明確にする。
1. 関数スコープ(var)
function example() {
if (true) {
var message = 'Hello';
}
console.log(message); // ✅ 'Hello'(ifの外でも見える)
}
- ✅
var
は関数の中すべてがスコープ - ❌ ブロック内に限定されない → 誤用によるリークが発生しやすい
2. ブロックスコープ(let / const)
function example() {
if (true) {
let msg = 'Hi';
const lang = 'JS';
}
console.log(msg); // ❌ ReferenceError
console.log(lang); // ❌ ReferenceError
}
- ✅
let
/const
はブロック({...})単位でスコープを制限 - ✅ 一時的な値・ループ変数・条件付き定義などで安全に扱える
3. スコープの選定基準
目的 | 選択 |
---|---|
再代入が必要・関数全体で使う | let |
再代入せずに定数として使う | const |
ES5互換が必要で最低限の用途のみ |
var (非推奨) |
4. for文とクロージャの落とし穴(varの副作用)
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// ❌ 3, 3, 3(ループ後のiが全てのクロージャに共有される)
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// ✅ 0, 1, 2(各ループのiが独立スコープ)
- ✅
let
によって ループごとにスコープが分離される - ❌
var
は同一スコープで参照が共有され、意図とずれる
5. スコープ制御による設計的メリット
- ✅ 意図しない変数のアクセス・再定義を防ぐ
- ✅ スコープが小さいほど影響範囲が限定され、安全性が向上
- ✅ メモリ効率やGC対象にも間接的に影響(スコープが短い変数は早期解放)
設計判断フロー
① 変数は関数全体で必要か、あるブロックでのみ必要か?
② 再代入する必要はあるか? → なければ const にする
③ for ループ内のクロージャは安全に処理できているか?
④ var を使っていないか? → 明示的に let/const へ置換
⑤ スコープの可視性を意識して、変数の影響範囲を限定しているか?
よくあるミスと対策
❌ if文内で var を定義し、意図せず外部に露出
→ ✅ ブロックスコープ let/const を用い、閉じた範囲で完結させる
❌ forループで var を使い、クロージャでバグ発生
→ ✅ ループ変数は let によりスコープをブロック単位に分離
❌ const を使うことに不安があり、全部 let にしている
→ ✅ const で「不変であること」を明示し、意味と制御を両立
結語
スコープとは「見えるかどうか」ではなく、
**“変数の責務と生存範囲を定義し、安全に管理するための設計境界”**である。
-
var
はもはや避けるべき歴史的存在 -
let
とconst
によって、設計上の意図をコードに織り込む - スコープの設計は「データの管理領域」を定義する最も根源的な構造戦略
JavaScriptにおけるスコープ制御とは、
“ロジックの責任と可視性を構造として明確化するための設計行為である。”