【JavaScript】クロージャについてざっくりまとめ
目的
JavaScriptのクロージャがについてざっくり理解すること
対象読者
- クロージャについて知りたい初学者
クロージャ(Closure)
JavaScript におけるクロージャとは、関数が外側のスコープにある変数を、その関数が外に持ち出されたり、後で呼び出されたりした後でも記憶し、アクセスできる仕組みです。
function createCounter() {
let count = 0; // ① 外側の関数のローカル変数
// ② この無名関数がクロージャ
return function() {
count += 1; // ① の count にアクセスし、更新
return count;
};
}
const myCounter = createCounter(); // ③ 'myCounter' はクロージャを保持する
console.log(myCounter()); // 1 が出力される
console.log(myCounter()); // 2 が出力される
特徴
-
createCounter()を呼び出し、中の関数を myCounter に格納している
→ myCounter には function() 無名関数が格納されている -
外側の関数 createCounter() の実行は終わっているのに、count 変数は消えずに、myCounter が呼び出されるたびに値を保持し続けている
これは「関数」と、その関数が「定義された時点の環境(レキシカル環境)」への参照を組み合わせたものと言えます。
環境の保持ではない
クロージャは、単にその環境(変数など)のスナップショット(静的なコピー)を保存しているわけではありません。もし、コピーを保持しているだけなら、クロージャを実行しても元の環境の変数を変更することはできません。
「環境への参照の保持」である
クロージャが持っているのは、定義された外側スコープにある変数そのものにアクセスするためのリンク(参照)です。
そのため、複数のクロージャが同じ外側スコープの変数を参照している場合、どれか1つがその変数を変更すると、他のすべてのクロージャから見える値もリアルタイムで更新されます。
function createCounter() {
let count = 0; // 参照される環境の変数
return {
increment: function() { // クロージャ 1
count += 1;
return count;
},
decrement: function() { // クロージャ 2
count -= 1;
return count;
}
};
}
const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
// decrement() が increment() と同じ 'count' の参照を持っているため、値が「2」から「1」に更新される
console.log(counter.decrement()); // 1
2つのクロージャは、どちらも同じ外側スコープの変数 count を参照しているため、一方の操作が他方に影響を与えます。
もし単なる「値のコピー」を保持しているだけなら、このような挙動は起こりません。
また、外側の関数(親関数)の実行が終了しても、その中に定義された関数(クロージャ)がその変数への参照を持ち続けている限り、その変数はメモリから解放されずに残り続けます。
これにより、実行が終わったはずの関数のローカル変数に後からアクセスできます。
まとめ
- クロージャ: 「関数」と「その関数が作られた環境」という2つのものの組み合わせ
- 適したケース: 特定のデータを保護したいとき、シンプルな関数を生成したい場合、単一の状態(変数)を保護しつつその状態を操作する機能の関数を返す場合など
最後までお読みいただき、ありがとうございました。
参考URL
Discussion