クロージャ

今回はJavaScriptの難関、クロージャについて書こうと思います。

わかったようでわかってない感じがするクロージャ
今回はとりあえずメモ。もっと深く理解したい。

まずはMDNからの引用。

クロージャは、独立した (自由な) 変数を参照する関数です。言い換えるとクロージャ内で定義された関数は、自身が作成された環境を '覚えています'。(MDN)

何言ってるかさっぱりですね。ナンノコッチャッて感じです。心折れそうです。
言葉だけだとわかりにくいので、実際のコードを見ていくことにします。

スコープ

クロージャを理解するのに必要なものとして、スコープが挙げられます。スコープとは簡単に言うと「変数を参照できる範囲」のことです。

スコープについては後日詳しく書くかも。

JavaScriptでは関数ごとにローカルスコープが作られます。
下記関数の変数 nameは関数 intro()の中でのみ参照することができます。関数 displayName() はintro()の内部にあるのでnameを参照することができます。

function intro() {
  var name = "Taro";

  function displayName() {
    console.log(name);
  }
  displayName();
}

クロージャ

クロージャの例としてよく見る感じの関数を書きます。

function intro() {
  var name = "Taro";

  function displayName() {
    console.log(name);
  }
  return displayName;
}

var introFunc = intro();

introFunc();

これでも前の関数と同じくTaroが表示されます。
これは関数 introが「関数を返す関数」になっていることを示しています。
ここでは以下の様な処理が行われているようです。

  1. intro()内で関数が生成される
  2. 変数displayNameにその値が代入される
  3. displayNameが戻り値として返される
  4. introFuncに代入される

関数なのか変数なのかはっきりしなくて気持ち悪いですが、JavaScriptではこのように変数のように扱えるみたいです。

クロージャの使いドコロ

クロージャの概念はなんとなく理解できましたが、では一体どんなときに使うのでしょう?
参考にさせていただいたMDNには下記のように書いてあります。

クロージャを使うと、データ (環境) をそれを操作する関数と結びつける事が出来ます。 ・・・中略 したがって、メソッドを 1 つだけ持つオブジェクトを使いたくなるような状況ならば、どんな時でもクロージャを使う事ができます。

ここでは2つのサンプルを見ていきたいと思います。

1. 自身が呼び出された回数をカウントする

function count(){
    var x = 1;

    return function (){
        console.log(x);
    x += 1;
    };

}

var f =  count(); 
f();  // 1
f();  // 2

2. モジュールパターン

モジュールパターンとはオブジェクトにパブリックなプロパティ、メソッドと、プライベートなプロパティ、メソッドを用意し、処理を切り分ける考え方です。これによりリファクタリングや機能の追加・削除が容易になります。
クロージャを利用することでプライベートなプロパティ、メソッドを作ることができます。

var counter = (function() {
  var privateCounter = 0;

  function changeBy(val) {
    privateCounter += val;
  }

  return {
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    },
    show: function() {
      console.log(privateCounter);
    }
  }; 
})();

counter.show(); // 0
counter.increment();
counter.increment();
counter.show(); // 2
counter.decrement();
counter.show(); // 1

変数 privateCounterと関数 changeByは両方ともプライベートな変数なので外側からアクセスすることはできません。
しかし、これを包んでいる無名関数から返されるパブリックメソッドを経由することでアクセスすることができます。

まとめ

クロージャの50%くらい理解できました。 あとは実践あるのみ?かな