- ベストアンサー
JavaScriptのクロージャの挙動の違い
- JavaScriptのクロージャには異なる挙動があります
- イベントハンドラのコピーではなく、関数の引数を利用することで問題を解決できます
- 正しいクロージャの使い方を理解することが重要です
- みんなの回答 (5)
- 専門家の回答
質問者が選んだベストアンサー
#3の返答です。 >共用 これは言葉通りの意味です。 var add_handler = function(nodes) { var i; // ←共用される変数i for(i = 0; i < nodes.length; i += 1) { nodes[i].onclick = function(e) { alert(i); // ←このiはクリックした時点のi }; } }; では nodes[0].onclick = function(e) { alert(i); }; nodes[1].onclick = function(e) { alert(i); }; … nodes[n].onclick = function(e) { alert(i); }; と、全てのノードのonclickイベントハンドラで同じ変数iが使われているので共用と書きました。 共用されているので、時系列で考えてクリックした時点のiは既にforループを脱している(add_handler関数も脱している)ので、nodes.lengthの値になります。 クロージャの特性上こんなことが言えると思います。 ・各ノードのonclickはadd_handlerのクロージャへの参照です ・そのクロージャで、上位スコープ(add_handler)の変数iを使っています ・add_handlerの実行すると、最終的に変数iはnodes.lengthの値になります ・クロージャはonclickの参照先とされたため、add_handler実行後も残ります。 ・そのクロージャで変数iが使われているため、add_handlerの実行後も変数iは生き残り、値も保持されます
その他の回答 (4)
- babu_baboo
- ベストアンサー率51% (268/525)
#1です 例の5番 var f = function(n){ return function() { alert(n); }; }(5); f(); //←これは引数がなくても5をアラーとするよね あなたが提示した nodes[i].onclick=function(i){ return function(e){ alert(i); }; }(i); を、1ぎょうにする。 nodes[i].onclick=function(i){return function(e){alert(i);};}(i); もし i=5 のとし、いわゆるくろーじゃのへんすうを i から n にすると nodes[5].onclick=function(n){return function(e){alert(n);};}(5); 最初の例5 var f = function(n){ return function() { alert(n); }; }(5); nodes[5].onclick = f; で、同じ。
お礼
改行を入れるとどこに;を入れるべきなのか混乱するので、 確かに1行で書くとそういうのはわかりやすいですね。 そして問題の本質もなんとか理解することができました。 ご協力ありがとうございました。
- imq
- ベストアンサー率72% (16/22)
理由は恐らく考えている通りだと思います。 各ノードのイベントハンドラは上位スコープにある変数iを共用しているので、ノードをクリックしたときにはすっかりforループを抜けていて、iはnodes.lengthの状態でアラートされます。 正しい方はfunctionを設けてスコープを一段下げることで、変数iの共用を避けています。 つまり、アラートしているiは上位スコープの変数iではなく、引数として与えられたiです。 for内のfunctionは、ループで実行するたびに動的に生成されるので、i=0の時とi=1の時は別のfunctionとなり、引数も生成されたときのものが保持されることになります。 変数名と引数が同じiだと紛らわしいので、 var add_handler = function(nodes){ var i; for(i = 0; i < nodes.length; i += 1) { nodes[i].onclick = function(j) { return function(e) { alert(j); }; }(i); } }; とした方が分りやすいでしょうか。 ちなみに最近では上記のようにクロージャを返すんじゃなくて、forループ内をfunctionでラップしてしまうことも多いです。 var add_handler=function(nodes){ var i; for(i = 0;i < nodes.length; i += 1) { function(j) { nodes[j].onclick = function(e) { alert(j); }; }(i); } }; JavaScriptではよく使う手法ですね。
お礼
ありがとうございます! 私は「変数を共用している」ということの意味が良く分かっていません。 本にも「実際の変数」みたいなことが書かれていて、何かそこが曖昧な感じがしています。
- fujillin
- ベストアンサー率61% (1594/2576)
ここでご短く説明するよりも、このあたりを(↓) http://builder.japan.zdnet.com/sp/javascript-kickstart-2007/?p=1 http://www.atmarkit.co.jp/fdotnet/ajaxjs/index/index.html
お礼
ありがとうございます! しっかりと読もうと思います。 クロージャの部分を読んでみたのですが、そこのクロージャは個人的には理解できていると思っています。 しかし、このイベントハンドラが来た場合にしっくりきません。。
- babu_baboo
- ベストアンサー率51% (268/525)
どこまでわかる? //_ alert(1); //_ var f = function(n) { alert(n); }; f(2); //_ (function(n) { alert(n); })(3); //____ var f = (function(n) { alert(n); })(4); // f = undefined //____ var f = function(n){ return function() { alert(n); }; }(5); // typeof f == 'function' f(); //____ (function(n){ return function() { alert(n); }; })(6)();
お礼
4つ目はalert(4)した戻り値がないからundefinedなのでしょうか。 5,6つ目は関数を返してそれを実行しているのかな、という認識が持てる程度です。 自分でこのようなコードは書けないと思います。
お礼
おおー! onclick=から先のfunctionの部分は、クリックされてからじゃないと読み込まれない。 普通のコードとは違う所で実行される認識。 このことを頭に入れたら完全に解決できました! だからイベントハンドラの時は気をつけないといけないのですね。 このスッキリした気分、最高です。 ありがとうございましたmm