クロージャがやっとこさ理解できたのでまとめ
クロージャって何ぞ?
メソッドが実行されるときに、メソッド内に参照する変数が無い場合、そのメソッドの呼び出し元ではなく、宣言を行ったスコープの変数を参照する仕組み
具体的には
<script> (function(){ var divs = $T("div"); // divタグのDOMを全て取得 for(var i=0,l=divs.length; i<l; i++){ divs[i].addEventListner( 'click', function(e){ alert(i); } ); } })(); </script> <div>0</div> <div>1</div> <div>2</div>
とかやると、divをクリックした時に、全部「2」が出力される理由です
なんで上のコードだと、alertの出力が2なの?
上記の場合、function(e)が参照している i は、宣言時の1つ上のスコープである無名functionのインスタンスが持っている
んで、function(e)を実行する時に、無名functionのインスタンスの i を調べると、for文がすで実行された後なので、i=2となるから
んじゃぁ、どうしたらよいの?
addEventListnerに渡すfunctionを別のスコープで囲ってやればOK
(function(){ var divs = $T("div"); // divタグのDOMを全て取得 for(var i=0,l=divs.length; i<l; i++){ divs[i].addEventListner( 'click', (function(i) return function(e){ alert(i); } )(i); ); } })();
こうしてやると、以下の理由で、それぞれの値がalertで出力されます
- function(e) が参照する i は、上のスコープの無名functionである function(i) の引数 i となる
- function(i) は、for文をまわしている時に実行されるので引数 i には、インクリメントに値が渡される
- 数値型、文字列型の引数は実体で渡されるので、function(i) 側で独自に値を持つことになる
- 実行されたときのfunction(i) インスタンスの中で値がそれぞれ保持されるので、function(e) 実行時に、それぞれの値がalertで出力される
なんで、毎回 function(i) が実行される毎に、i の値は上書きされないの?
function(i) が実行されたときに、戻り値である関数 function(e)は 変数 i を参照する時の為に、実行時のfunction(i)のインスタンスへの参照を持ってるっぽいです
なので、戻り値としては、関数 function(e) 単体ですが、実行時の上のスコープの変数の値が保持されるという仕組みみたいです
ややこしいね、この仕組みだとなんか得なの?
関数型言語的に、関数の引数を増やしたり、ライブラリに独自のメソッドを渡したりすることが簡単にできるようになります
- STLの関数オブジェクトと似た思想だと思うです
例えば、下の例ではクロージャを利用してsetIntervalに渡すメソッドに、任意のオブジェクトを渡してます
var obj = {text: "あああ", int : 123}; setInterval( (function(obj){ return function(e){ // あああ、123と出力 alert(obj.text, obj.int); }; })(this), 1000
大体そんな感じです