概要
クロージャ(Closure)は、JavaScriptの関数がスコープ外の変数を保持し続ける能力を指す。
それは「関数の外で定義された変数が、関数の内側で使える」という表面的な便利さを超え、**状態と関数の統合(=関数による状態のカプセル化)**を実現する。
本記事では、クロージャの本質・使い所・メリット・設計上の注意点を構造的に整理し、ただ使うだけでなく、使いこなすための戦略を提示する。
クロージャとは何か?
function createCounter() {
let count = 0;
return function () {
count++;
return count;
};
}
const counter = createCounter();
counter(); // 1
counter(); // 2
- ✅
count
はcreateCounter()
のスコープ内で定義された変数 - ✅ 内部関数が
count
を“捕捉(close over)”して保持している
→ これがクロージャ
クロージャが使われる代表的パターン
✅ 状態のカプセル化(データの隠蔽)
function userFactory(name) {
let secret = 'classified';
return {
getName: () => name,
getSecret: () => secret,
setSecret: (s) => { secret = s; }
};
}
const u = userFactory('toto');
u.getSecret(); // 'classified'
u.setSecret('X'); // 更新可
→ ✅ secret
は外部から直接アクセスできず、関数経由でのみ操作可能
✅ 関数ファクトリ(設定を固定した関数を生成)
function multiplier(factor) {
return (n) => n * factor;
}
const double = multiplier(2);
double(5); // 10
→ ✅ パラメータを持った「関数の雛形」が生成できる
✅ コールバック内での状態保持
function setupLogger() {
let logs = [];
return function (msg) {
logs.push(msg);
console.log(logs.join(' / '));
};
}
const logger = setupLogger();
logger('A'); // A
logger('B'); // A / B
クロージャを使うメリット
効果 | 説明 |
---|---|
データの隠蔽 | 直接アクセスできない変数として保持 |
状態の永続化 | 関数が実行された後も値が保持され続ける |
高度な再利用性 | パラメータを記憶した関数を生成できる |
コールバックの文脈維持 |
setTimeout , Promise 内などで状態を維持可能 |
設計上の注意点
❌ 意図しないメモリ保持(メモリリーク)
function leak() {
let huge = new Array(1_000_000).fill('x');
return () => huge;
}
- ✅
huge
はGCされない → 無駄にメモリを消費 - ❌ 呼び出されない関数でも変数を保持し続ける
→ 🔧 対策:スコープの寿命を最小限に設計
❌ クロージャで値が“固定”されていない場合の誤解
let funcs = [];
for (var i = 0; i < 3; i++) {
funcs.push(() => i);
}
funcs[0](); // 3 ❌
funcs[1](); // 3 ❌
→ 🔧 対策:let
or IIFEでスコープを分離
for (let i = 0; i < 3; i++) {
funcs.push(() => i); // ✅ 0, 1, 2
}
クロージャの使用判断フロー
① 状態を外部から隠したい? → クロージャでカプセル化
② 関数に設定を埋め込みたい? → クロージャで関数ファクトリ
③ コールバック内で値を保持したい? → クロージャで値を囲い込む
④ 状態が巨大 / 不定多数? → クロージャは避け、構造体や外部記憶を使う
結語
クロージャは、単なる便利な“テクニック”ではない。
それは「関数に状態を持たせ、設計に責任を与える構文」である。
- 関数が“値を記憶する”ことで、一貫性と隠蔽性を同時に実現
- 外部から見えないスコープが、ロジックの防御壁となる
- ただし、“覚えてしまう”がゆえに、適切な開放・スコープ設計が不可欠
クロージャとは、“設計された記憶”である。