クロージャ
関数が定義時のスコープの変数を記憶し、外部から参照できる仕組み
JavaScript関数型プログラミング
クロージャとは
クロージャ (Closure) は、関数が定義時のスコープ (レキシカルスコープ) の変数を記憶し、そのスコープの外から参照できる仕組みである。JavaScript の関数は全てクロージャ。
基本例
function createCounter() {
let count = 0; // この変数がクロージャで記憶される
return {
increment: () => ++count,
getCount: () => count,
};
}
const counter = createCounter();
counter.increment(); // 1
counter.increment(); // 2
counter.getCount(); // 2
// count は外部からアクセスできないが、返された関数からは参照可能
なぜ動くのか
createCounter() 実行時:
1. count = 0 がスコープに作られる
2. increment, getCount が count への参照を持つ
3. createCounter() の実行は終了するが、
返された関数が count を参照しているため GC されない
→ これがクロージャ
実用例: プライベート変数
function createLogger(prefix: string) {
return (message: string) => console.log(`[${prefix}] ${message}`);
}
const log = createLogger('API');
log('Request received'); // [API] Request received
// prefix は外部から変更できない
実用例: イベントハンドラ
function setupButton(id: string) {
let clickCount = 0;
document.getElementById(id)?.addEventListener('click', () => {
clickCount++;
console.log(`Clicked ${clickCount} times`);
});
// コールバック関数が clickCount をクロージャで記憶
}
よくある落とし穴: ループとクロージャ
// ❌ var はブロックスコープを持たない
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100); // 3, 3, 3
}
// ✅ let はブロックスコープ (各反復で新しい変数)
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100); // 0, 1, 2
}
Rust のクロージャ
let x = 5;
let add_x = |n| n + x; // x をキャプチャ
add_x(3) // 8
// Rust はキャプチャ方法を区別: &T, &mut T, T (所有権の移動)
let add_x = move |n| n + x; // x の所有権を移動
クロージャの活用場面
| 場面 | 説明 |
|---|---|
| プライベート変数 | 外部からアクセスできない変数 |
| イベントハンドラ | コールバックで外部変数を参照 |
| カリー化 | 引数を部分適用 |
| Lambda ハンドラ | ハンドラ外の変数を再利用 |
| メモ化 | キャッシュをクロージャで保持 |
理論と実装の両面から学ぶなら関連書籍が参考になる。