概要
JavaScriptにおける関数は、単なる処理のブロックではない。
それは、スコープと状態を「閉じ込める」構造を持つ、一種のオブジェクトである。
この“関数が状態を保持する”という特徴の本質が、**クロージャ(Closure)**だ。
「関数が変数を覚えている」という一言では説明しきれないその挙動を、構造・記憶・責任の設計という視点で再定義する。
対象環境
ES5以降のすべてのJavaScript環境(Node.js / ブラウザ)
クロージャとは何か?
関数が、自身のスコープ外の変数を“保持し続ける”機能。
✅ 具体例:
function counter() {
let count = 0;
return function () {
count++;
return count;
};
}
const increment = counter();
console.log(increment()); // 1
console.log(increment()); // 2
→ count
は counter
の中で定義されたが、返された関数がそれを保持し続けている
なぜクロージャが生まれるのか?
クロージャの発生条件:
- 関数がネストされている
- 内側の関数が、外側の変数を参照している
- 外側の関数が終了しても、内側関数がどこかで参照されている
function outer() {
let secret = 'inside';
return function inner() {
console.log(secret);
};
}
→ inner
は secret
を“記憶している”
実務での用途①:状態管理
クロージャを使えば、外部からアクセスできない状態を内部に保持し続けられる。
function createStore(initial = 0) {
let state = initial;
return {
get: () => state,
set: (v) => (state = v),
reset: () => (state = initial)
};
}
const store = createStore(10);
store.set(42);
console.log(store.get()); // 42
store.reset();
console.log(store.get()); // 10
→ state
という値は、外部からは直接触れず、メソッド経由でしか操作できない
実務での用途②:関数のカスタマイズ・カリー化
function multiply(factor) {
return function (value) {
return value * factor;
};
}
const double = multiply(2);
console.log(double(10)); // 20
→ factor
を記憶しているので、部分適用された関数を生成できる
実務での用途③:イベントリスナーのスコープ隔離
for (let i = 0; i < 3; i++) {
setTimeout(function () {
console.log(i);
}, 100);
}
→ ES6の let
がブロックスコープなので問題ないが、var
ではクロージャがなければ正しく動作しなかった。
落とし穴と注意点
❌ メモリリーク
クロージャは「状態を保持し続ける」がゆえに、意図せず変数を解放できないことがある。
function hold() {
let bigData = new Array(1000000).fill('...');
return () => bigData;
}
const ref = hold();
// `bigData` は GC 対象外になる
→ ✅ 不要になった関数への参照は明示的に破棄する(nullにするなど)
❌ thisの扱いに注意
function outer() {
let name = 'toto';
return function () {
console.log(this.name); // undefined
};
}
→ クロージャは this
ではなくスコープ変数を閉じ込める
→ ✅ this
が必要な場合は bind
やアロー関数を使う
設計戦略:クロージャを使うべき場面とは
要件 | クロージャの使用 | 理由 |
---|---|---|
内部状態を持ちたい | ✅ 適している | オブジェクトを使わずに状態保持できる |
他から値を隠したい | ✅ 有効 | 外部アクセスを排除できる |
毎回初期化された関数が欲しい | ✅ 最適 | スコープ毎に独立した状態が生成される |
複雑すぎる状態を管理したい | ❌ 不適 | ReactやVueなど、構造的に管理すべき |
結語
クロージャはJavaScriptの“ふるまい”ではなく、“設計機構”である。
その本質は「状態のカプセル化」と「スコープの延命」にある。
それはただのトリックではなく、純粋関数と副作用の間にある、ミニマルで力強い表現手段だ。
意図してクロージャを使い、状態を設計し、記憶を制御できること。
それが、プロフェッショナルなJavaScript設計者の第一歩である。