ループ内の無名関数
スーパーなエンジニアの近くに座っているMODです。
こないだJavascriptを書いててハマったネタをひとつ。
for文等繰り返し文の中で無名関数を定義するとき、定義内でインデックスの変数の値を用いることができないらしいです。
例えばこんなコード
<input type='button' id='wan1' value='わん'>
<input type='button' id='wan2' value='わんわん'>
<script type="text/javascript">
for (i=1; i<=2; i++) {
elem = document.getElementById('wan'+i);
elem.attachEvent('onclick', function(){alert('いぬ No.'+i);});
}
</script>
※このコードはIEのみ対応です。
ボタンをクリックすればそれぞれ「いぬ No.1」、「いぬ No.2」とアラートで表示させる意図ですが、実際にはどっちをクリックしても表示されるのは「いぬ No.3」という文字列。
どうやら i の値は関数を呼び出した時点で評価されているようです。
実行時に関数に渡されるイベントオブジェクトを用いてなんとかするしか今のところ解決策がみつかっていません。Javascript難しいです。

コメント
これならどうでしょうか?
for (i=1; i<=2; i++) {
elem = document.getElementById('wan'+i);
elem.attachEvent('onclick', createHandler(i));
}
function createHandler(index) {
return function(){ alert('いぬ No.'+index); };
}
投稿者: cesare | 2006年5月18日 21:17
elem.attachEvent('onclick', (function(i){return function(){alert('いぬ No.'+i);}}).call(this, i));
これでうまくいきますね。
最初の無名関数は関数オブジェクトを返すための無名関数です。
その場で実行したいので、call を用いて引数 i を渡して受け取った引数を元に生成した関数オブジェクトを返しています。
原理的に cesare さんとやってることは同じですが、
全部無名関数で実行できます。
投稿者: masato | 2006年5月19日 02:14
>cesareさん
コメント&トラックバックありがとうございます!
直接関数への参照を置くと後から実行されるから意図どおりの値になりませんが、attachEventが呼ばれた際に引数の中で関数を実行すれば、適切な値が反映された無名関数が返るということですね。
Javascriptに慣れていないこともあって、関数オブジェクトを戻り値にする関数という発想が浮かびませんでした。勉強させていただきました。
>masatoさん
理解するのに時間がかかりました。もう、何やら別の言語と化してる気がします。でも多分これがJavascriptなんですね。
最初の無名関数の仮引数はnとかでもよいというのがミソでしょうか。
javascriptのcallも知りませんでした・・・。自分勉強不足です。__o_
投稿者: MOD | 2006年5月19日 13:46
更に別解
elem.attachEvent('onclick', new Function("", "alert('いぬ No.'+"+i+");"));
Javascript では関数というのは単なるオブジェクトであるということ、
function(){} という書き方は Function オブジェクトを new するためのリテラルでしかないというのがポイント。
ていうかこっちの方が簡潔やね
投稿者: masato | 2006年5月19日 17:26
>masatoさん
javascriptのパーサはfunction(){....}を見ると
その関数の本体をその場では読まなくなるみたいだから、
evalを使って強引にすれば・・・
eval("var f = function(){alert('いぬ No."+i+"');};");
// var f = function(){alert('いぬ No.'+i);};
// は動かない
elem.attachEvent('onclick', f);
#elem.attachEvent('onclick', eval('function(){...}'));
#はさすがに動きませんでした^^;
余談ですがjavaで内部クラスを用いて同様のことをしようとすると、
“内部クラスからfinalでない変数にはアクセスできない”
とコンパイラに怒られました。
投稿者: MOD | 2006年5月19日 18:50
JavaScriptに引数の省略機能はないのでしょうか?もしあればそれを使って簡潔な表記ができそうだと思いました。説明のためにコードを書いたらたくさん書きすぎてしまったので詳しい内容はリンク先をご覧ください。
http://www.nishiohirokazu.org/blog/2006/05/re.html
投稿者: nishio | 2006年5月30日 16:05