クロージャ

関数が定義時のスコープの変数を記憶し、外部から参照できる仕組み

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 ハンドラ ハンドラ外の変数を再利用
メモ化 キャッシュをクロージャで保持

理論と実装の両面から学ぶなら関連書籍が参考になる。

関連用語