🦔

【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

https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Closures

https://qiita.com/saka212/items/8dbbb524a4fe86572d42

https://qiita.com/takeharu/items/4975031faf6f7baf077a

Discussion