JavaScriptにおけるクロージャーとは??




個人的にJSのクロージャ理解が微妙だったので
        まとめる。
1.Closureとはcloseする・・
                つまり閉じ込めちゃうことを言う。


まず普通の関数。
function f(){
      var value = 普通の関数 ;
      console.log(value);
}
これは、グローバル空間に関数fを定義し、
更にそのグローバル関数f内にローカル変数valueを定義している。
この場合、普通にローカル変数valueが
出力される。
2.関数呼び出し時に
              いったい何が起きてるの?




JavaScriptの仕様において、JS上で関数を呼び出すと、
実は内部的にJSのソース上ではコントロール不可能な
暗黙のオブジェクト【Call】というオブジェクトが生成されてるようだ。
この件に関しては、
下記
「パーフェクトJavascript」
が詳しい。
3.Callオブジェクトとはなんぞや?
                  そして、生成タイミングとは?


先ほどの関数
function f(){
       var value = 普通の変数 ;
       var g = function ( ) { /* 任意の関数 */ } ;
       console.log(value);
}
これを呼び出す時、実はCallF(仮)という
オブジェクトが生成される。


f();   //この時、関数f内部でCallFが生成される


//しかし、呼び出し直後すぐにCallFはガベコレの対象となる。
考え方としては
function f(){
      var value = 普通の変数 ;
      var g = function ( ) { /*任意の関数*/ };
      console.log(value);
}
とある関数が
function f(){
      //CallF = {}; 関数内部でCallオブジェクトが暗黙生成
      //CallF.value = 普通の変数 ;
      //CallF.g = function ( ) { /*任意の関数*/ };
      //console.log(CallF.value);
      var value = 普通の変数 ;
      var g = function ( ) { /*任意の関数*/ };
      console.log(value);
}
と、なっているイメージ。
Callオブジェクトというのは、ある任意の関数内で定義された
         すべてのローカル変数をプロパティとして保持する
          暗黙のオブジェクトとして一般に定義される。


このCallオブジェクトというのは、関数が呼び出された
直後に、暗黙のうちに生成され(暗黙のうちにローカル変数を
自身のプロパティとして保持し)関数の呼び出しを終えたあと、
暗黙のうちに消滅する。つまりは、


//関数の呼び出し
f ( );   //CallFオブジェクトが生成される。


//その後、呼び出し終了後CallFオブジェクトは
//消滅する。
これを踏まえた上で次の手順へ。
4.クロージャとエンクロージャー



まず、言葉の整理。
Javascriptでは、関数の中に関数を定義(宣言)する
ことができる。
この時、外側の関数をエンクロージャーと呼び、
内側の関数をクロージャと呼ぶ。


クロージャの正確な意味は、変数を閉包するという
ところに真の意味があるが、ここでは便宜上の
呼び方として定義する。


外側の関数 =>エンクロージャ
内側の関数 =>クロージャ
5.関数の中に関数を定義する



function f( ){
      var value =エンクロージャ側の変数 ;
      function g ( ){
    //クロージャー側の内容
          console.log(value);
      }
      g( );
}


上記のような関数の場合、関数fをよびだすと
そのまま間接的に関数gを呼び出すことと同義になる。
この時、関数gはエンクロージャのローカル変数を
参照することが可能である。
これはスコープチェインと呼ばれている。
関数fを呼び出すと前述したとおりCallFオブジェクトが
生成されると考えることができる。
そして、JSのスコープチェインという仕様上内部関数の
関数gはこのCallFオブジェクトの【参照】を持っている。


また当然、関数gが呼び出されると関数gの中でも
CallGオブジェクトが暗黙のうちに生成される。
今回の例ではCallGオブジェクトにプロパティの類は
持っていないことになる。
6.スコープチェイン




Javascriptにはスコープチェインっていう仕様が存在する。


スコープチェインっていうのは、関数の中に関数を定義した場合、
内部の関数はその直近の外側関数のローカル変数
(外側関数のローカル変数をプロパティに持つCallオブジェクト)
を参照することができることを言う。


つまり、
「外側関数の暗黙オブジェクト【CallF】の参照を内側関数が保持」
していると言い換えることができる。
7.もう少し詳しく見ていこう


function f( ){
         var value =  エンクロージャ側の変数 ;
         function g ( ){
    //クロージャー側の内容
             console.log(value);
         }
         g( ); //②CallGオブジェクトが生成
   //③CallGオブジェクトが消滅
}


f ( );       //①この時、CallFオブジェクトが生成
//④ f( )の呼び出し終了後 CallFオブジェクトが消滅
④の時点でCallFオブジェクトが保持していた関数fのローカル変数
変数【value】および 関数【g】は参照元のCallFオブジェクトが
消滅するため、ガベージコレクションにより同時に消滅していると
言える。
②の関数gが呼び出された時、関数gは関数fの
呼び出し時に生成されたCallFオブジェクトへの参照を
持っていることを覚えておく。


では、先ほどの関数を以下のように定義しなおしてみる。


function f( ){
      var value =エンクロージャ側の変数 ;
      function g ( ){
    //クロージャー側の内容
          console.log(value);
      }
      //今度はCallF.gをリターンしてみる。
      return g;
}
今度は、関数f内部の関数gを関数内で実行する
のではなく、returnキーワードで関数オブジェクトg
として返してみる。つまりは以下のようになる。


var g2 = f ( ); //①CallFオブジェクトの生成


//②CallFオブジェクトの消滅・・・・?


g2( ); //③内部のCallF.g関数が実行されCallGオブジェクトの生成


//④g2( )の呼び出し後、CallGオブジェクトの消滅


今までの内容で考えるとf( ) と関数fが呼び出されて呼び出し終了後
内部関数gは、CallFオブジェクトの消滅と同時にガベコレされる
はずである。しかしながら②で消滅したはずの関数gを③で呼び出す
ことができている。これはどういうことだろうか?
var g2 = f ( ); //①CallFオブジェクトの生成
//②CallFオブジェクトの消滅・・・・?


ここの段階でCallFが消滅するかに見えるが
実際は内部関数gが返り値として変数【g2】に参照されている。
この内部関数gをグローバルな変数【g2】が参照するということが
何を意味するかというと?
【6.スコープチェイン】の項目で内部関数(クロージャー)は
親関数の暗黙オブジェクトCallFの参照を保持していると述べた。


つまり、上記②の段階でCallFオブジェクトはグローバル変数g2が
内部関数gを保持することによってその存在を継続することができる。
結果
g2( );
と内部関数をグローバルスコープで実行することによって
アクセス不可能なはずのCallFオブジェクトに関数gを介して
CallFへアクセスすることができる。
これが クロージャーというわけであーる。
8.締め
JavaScriptにおけるクロージャーとは?


①グローバルスコープ上で定義されたある任意の関数f内の
ローカルスコープ上で定義された関数gをグローバルな任意の変数【g2】へ
関数fの実行結果(returnする)として返す。


②これまで述べたように、関数f内で定義された関数gはその
「親関数fのローカル変数すべてをプロパティとして所持する
暗黙オブジェクトCallFの参照」
を保持する事ができる。これをスコープチェインと言う。


③グローバル変数【g2】が内部関数gへの参照を保持し続けるかぎり
内部関数gは親関数fの暗黙オブジェクトCallFを保持し続ける。


④つまり変数【g2】は内部関数gを窓口にすることによって本来はアクセス
不可なはずの関数fのローカル変数をグローバルスコープからアクセスする
ことが可能になる。これがクロージャーである。

クロージャー