「クロージャは難しい」
JavaScriptやTypeScriptを学んでいると、必ずと言っていいほど出てくる言葉です。
- 関数の中で関数を定義する
- 変数を保持し続ける
- スコープがどうこう
説明を読んでも、正直ピンと来ない人は多いはずです。
私自身、これまで肉体労働中心の仕事をしてきましたが、未経験からIT企業への転職が決まり、TypeScriptの学習を進める中でクロージャに出会いました。
最初は「知識としては読んだけど、いつ使うのか分からない」という状態でした。
しかし、ToDoリストやイベント処理、状態管理を実装する中で、
「あ、これクロージャじゃん」
と後から理解が追いついてきました。
結論から言うと、クロージャは
「関数が変数を記憶し続ける仕組み」
です。
クロージャとは何かを一言で説明すると
クロージャとは、
関数が定義されたときのスコープ(変数の状態)を保持し続ける仕組み
です。
もう少し噛み砕くと、
関数が終わったあとでも、その中で使っていた変数を覚えている状態
と言えます。
この「覚えている」という感覚がつかめないと、クロージャは一生分からないままになります。
なぜクロージャが分かりにくいのか
変数は関数が終わると消えると思ってしまう
初心者の頃は、
- 関数が終わったら中の変数は消える
- 次に呼ばれたら初期化される
と考えるのが自然です。
しかし、JavaScriptでは
関数が別の関数から参照されている限り、変数は保持されます。
この仕様が、クロージャの正体です。
「クロージャを使っている自覚がない」
多くの場合、クロージャは
意識せずに使っている ものです。
特に次のような場面では、ほぼ確実にクロージャが使われています。
- イベントリスナー
- setTimeout / setInterval
- 状態を内部に持つ関数
知らないうちに使っているからこそ、説明されると混乱します。
簡単なコードでクロージャを理解する
まずは最小限の例です。
function createCounter() {
let count = 0;
return function () {
count++;
return count;
};
}
const counter = createCounter();
counter(); // 1
counter(); // 2
counter(); // 3
ここで起きていることを整理します。
createCounterは一度だけ実行されるcountは通常なら消えるはず- しかし、返された関数が
countを参照している - そのため
countは保持され続ける
この 「保持され続ける状態」 がクロージャです。
イベント処理とクロージャの関係
クロージャは、イベントデリゲーションとも深く関係しています。
let taskId = 0;
button.addEventListener("click", () => {
taskId++;
console.log(taskId);
});
このコードでは、
- クリックされるたびに
taskIdの値が増え- 前回の状態が保持されます
ここでも、イベントリスナーの中で
外側の変数を覚え続けている
というクロージャが使われています。
イベントが「後から実行される処理」である以上、
クロージャなしでは成立しません。
クロージャは「状態管理」の基本
ReactやTypeScriptを学んでいくと、
- state
- hook
- カスタムフック
といった概念が出てきます。
これらの根っこには、
「値を安全に保持し続ける仕組み」
があります。
クロージャは、その最小単位です。
DOM操作でToDoリストを作っていたときも、
- 配列をどこで持つか
- 値をどう更新するか
といった設計は、すべて
クロージャ的な考え方 に近いものでした。
クロージャを「使いこなそう」としなくていい
初心者のうちは、
「クロージャを使ったコードを書こう」
と意識する必要はありません。
重要なのは、
- なぜ値が保持されているのか
- なぜ前回の結果を覚えているのか
を説明できるようになることです。
その説明ができれば、クロージャは理解できています。
まとめ
クロージャとは、
- 関数が
- 定義されたときの
- 変数を覚え続ける仕組み
です。
最初は難しく感じますが、実は
- イベント処理
- 状態管理
- UI更新
あらゆる場面で自然に使われています。
未経験からTypeScriptを学ぶ中で感じたのは、
クロージャは「知識」より「感覚」が大事 だということです。
DOM操作 → React → 状態管理と進む中で、
必ず何度も立ち返る概念なので、
今の段階で「なんとなく分かった」で十分です。

コメント