クロージャとは
クロージャとは、関数とその関数が作られた時の環境(スコープ)を組み合わせたものです。
もっと簡単に言うと、関数が自分の外側にある変数を記憶し続ける仕組みのことです。
前提知識:スコープの基本
クロージャを理解する前に、スコープについて簡単に確認しましょう。
スコープとは変数が参照できる範囲のことで、JavaScriptでは内側から外側の変数にアクセスできます。
const global = 'グローバル';
function outer() {
const outerVar = '外側';
function inner() {
const innerVar = '内側';
console.log(innerVar); // '内側' - 自分のスコープ
console.log(outerVar); // '外側' - 親のスコープ
console.log(global); // 'グローバル' - グローバルスコープ
}
inner();
}
outer();
通常、関数の実行が終わると、その関数内で宣言した変数はメモリから削除されます。しかし、クロージャを使うと変数を保持し続けることができるんです。
クロージャの基本例
最もシンプルなクロージャの例を見てみましょう。
function createGreeting(name) {
return function() {
console.log(`こんにちは、${name}さん`);
};
}
const greetTaro = createGreeting('太郎');
const greetHanako = createGreeting('花子');
greetTaro(); // 'こんにちは、太郎さん'
greetHanako(); // 'こんにちは、花子さん'
ここで注目すべきポイントは、createGreetingの実行が終わった後も、返された関数がnameという変数にアクセスできていることです。
実行の流れ
なぜ変数が保持されるのか
JavaScriptのメモリ管理は、以下のルールで動作します。
「どこかから参照されている変数は、メモリに残り続ける」
function createCounter() {
let count = 0; // ①この変数が作成される
return function() { // ②この関数がcountを参照している
count++;
return count;
};
}
const counter = createCounter();
// createCounterの実行は終わったが、
// 返された関数がcountを使っているので、
// countはメモリに残り続ける
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
メモリ管理の仕組み
クロージャの実用例
例1: カウンター機能
function createCounter() {
let count = 0;
return {
increment: function() {
count++;
return count;
},
decrement: function() {
count--;
return count;
},
getValue: function() {
return count;
}
};
}
const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.decrement()); // 1
console.log(counter.getValue()); // 1
count変数は外部から直接アクセスできず、用意されたメソッドを通してのみ操作できます。これをプライベート変数と呼びます。
例2: イベントハンドラ
function setupButtons() {
const buttons = document.querySelectorAll('.btn');
buttons.forEach(function(button, index) {
button.addEventListener('click', function() {
console.log(`ボタン${index}がクリックされました`);
});
});
}
setupButtons();
各イベントハンドラは、それぞれ異なるindexの値を記憶しています。これもクロージャの働きですね。
例3: 関数ファクトリー
function createMultiplier(multiplier) {
return function(number) {
return number * multiplier;
};
}
const double = createMultiplier(2);
const triple = createMultiplier(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15
同じ関数構造でも、保持しているmultiplierの値が異なるため、異なる動作をします。
クロージャの動作を図で理解する
複数のクロージャが作られた場合、それぞれ独立した変数を持ちます。
function createCounter() {
let count = 0;
return function() {
count++;
return count;
};
}
const counter1 = createCounter();
const counter2 = createCounter();
console.log(counter1()); // 1
console.log(counter1()); // 2
console.log(counter2()); // 1 ← counter1とは別のcount
まとめ
クロージャの重要なポイントは以下の3つです。
変数の保持
関数が外側の変数を参照している場合、その変数は関数が存在する限り保持される
独立性
各クロージャは独自の変数のコピーを持ち、互いに影響しない
用途
プライベート変数、状態の保持、関数ファクトリーなど、幅広い用途で活用できる
クロージャはJavaScriptの強力な機能の一つです。最初は難しく感じるかもしれませんが、「関数が変数を記憶し続ける仕組み」と理解すれば、徐々に使いこなせるようになります。実際にコードを書いて、動作を確認しながら学んでいきましょう。