クロージャとスコープ
スコープは変数が見える範囲、クロージャは関数が定義時の外側の変数を“閉じ込めて”覚え続ける仕組み。状態を持つ関数を作れます。
- 1.スコープとは変数が参照できる範囲で、グローバル・関数・ブロックの階層で決まります。
- 2.クロージャは関数が定義された場所の外側変数を保持し続ける仕組みで、状態を閉じ込められます。
- 3.ループ内で変数を共有すると意図しない値を全クロージャが参照する罠があり、ブロックスコープで回避します。
スコープ:変数が見える範囲
スコープとは、ある変数を「どこから参照できるか」という範囲のことです。範囲を区切ることで、名前の衝突を防ぎ、変数の影響を局所化できます。多くの言語は次の3階層を持ちます。
- グローバルスコープ — プログラム全体から見える。便利だが、どこからでも書き換えられて事故のもと。
- 関数スコープ — その関数の中だけで有効。引数や関数内で宣言した変数。
- ブロックスコープ —
{ }の中だけで有効。ifやforの内側など。
内側からは外側の変数が見えますが、外側から内側の変数は見えません。この「内→外は見える」性質がクロージャの土台になります。
const g = "global";
function outer() {
const f = "function";
if (true) {
const b = "block";
console.log(g, f, b); // 3つとも見える(内側から外を参照)
}
// console.log(b); ← ここでは b は見えない(エラー)
}
var と let の違い
JavaScript では宣言キーワードでスコープが変わります。var は関数スコープ、let と const はブロックスコープです。この違いが後述のループの罠に直結します。
| キーワード | スコープ | 再代入 | 備考 |
|---|---|---|---|
| var | 関数スコープ | 可 | ブロックを無視する。巻き上げあり |
| let | ブロックスコープ | 可 | 現在の標準的な書き方 |
| const | ブロックスコープ | 不可 | 再代入しない値はこちらを優先 |
再代入しない変数は const、する変数だけ let にすると、変更箇所が読み手に伝わり、意図しない上書きも防げます。var は新規コードでは基本的に使いません。
クロージャ:外側の変数を閉じ込める
クロージャとは、関数が「定義された場所の外側の変数」を覚えたまま持ち運ぶ仕組みです。関数を呼ぶたびに値が消えるのではなく、外側の変数を生かし続けます。これにより、関数が内部に状態を持てます。
function makeCounter() {
let count = 0; // 外側のローカル変数
return function () {
count += 1; // 外側の count を覚えている
return count;
};
}
const next = makeCounter();
next(); // 1
next(); // 2 ← count が保持され続けている
makeCounter() の実行はとうに終わっているのに、返された関数は count を握り続けます。これがクロージャです。count は外から直接触れないため、カプセル化された状態としても働きます。
ループ内クロージャの罠
クロージャで最も有名なつまずきが、ループの中で関数を作るケースです。var を使うと、各関数が同じ1つの変数を共有してしまいます。
const fns = [];
for (var i = 0; i < 3; i++) {
fns.push(function () { return i; });
}
fns[0](); // 0 ではなく 3 が返る!
ループ終了後 i は 3 になっており、3つの関数すべてが同じ i を見ているため、全部 3 を返します。var が関数スコープで、毎回の繰り返しで変数が共有されるのが原因です。
var は1個の変数を全員で共有します。let は繰り返しごとに新しい変数を作るため、各クロージャが当時の値を覚えます。下のように let へ変えるだけで期待どおり 0, 1, 2 になります。
const fns = [];
for (let i = 0; i < 3; i++) { // let なら毎回 i が新規に束縛される
fns.push(function () { return i; });
}
fns[0](); // 0
fns[1](); // 1
まとめ
スコープは「変数が見える範囲」、クロージャは「関数が外側の変数を閉じ込めて覚える仕組み」です。クロージャのおかげで、カウンターや設定のように状態を内側に隠した関数を作れます。ループで関数を量産するときは、変数が共有されるか毎回新規かを意識し、**ブロックスコープ(let)**を使えば古典的な罠を避けられます。
プログラミング Article
クロージャとスコープを実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
クロージャ
比較で見る軸
難易度: intermediate / カテゴリ: プログラミング / タグ数: 3
導入後に効く点
クロージャは関数が定義された場所の外側変数を保持し続ける仕組みで、状態を閉じ込められます。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- intermediate
- カテゴリ
- プログラミング
- タグ数
- 3
判断チェックリスト
- 自社の用途が「クロージャ / スコープ」に近いか確認する。
- 強みである「スコープとは変数が参照できる範囲で、グローバル・関数・ブロックの階層で決まります。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。