« 「超・極めるPHP!」本日発売 | メイン | キーボードなトリビア »

ループ内の無名関数
このエントリーをはてなブックマークに追加 このエントリーをlivedoorクリップに追加

スーパーなエンジニアの近くに座っている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難しいです。

トラックバック

このエントリーのトラックバックURL:
http://www.unoh.net/mt32/mt-tb.cgi/127

この一覧は、次のエントリーを参照しています: ループ内の無名関数:

» 変数スコープ@JavaScript from 青方偏移
ウノウラボさんのエントリーに何気なくコメント付けて、後からよく見直したらコードが一部消されていたので、こちらに書きます。「&lt;」が混じってるの... [詳しくはこちら]

» Javascript ループ中にイベントリスナーを登録する最も簡単な方法 from へっぽこ開発室
今回は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); };
}

elem.attachEvent('onclick', (function(i){return function(){alert('いぬ No.'+i);}}).call(this, i));

これでうまくいきますね。
最初の無名関数は関数オブジェクトを返すための無名関数です。
その場で実行したいので、call を用いて引数 i を渡して受け取った引数を元に生成した関数オブジェクトを返しています。

原理的に cesare さんとやってることは同じですが、
全部無名関数で実行できます。

>cesareさん
コメント&トラックバックありがとうございます!
直接関数への参照を置くと後から実行されるから意図どおりの値になりませんが、attachEventが呼ばれた際に引数の中で関数を実行すれば、適切な値が反映された無名関数が返るということですね。
Javascriptに慣れていないこともあって、関数オブジェクトを戻り値にする関数という発想が浮かびませんでした。勉強させていただきました。

>masatoさん
理解するのに時間がかかりました。もう、何やら別の言語と化してる気がします。でも多分これがJavascriptなんですね。
最初の無名関数の仮引数はnとかでもよいというのがミソでしょうか。
javascriptのcallも知りませんでした・・・。自分勉強不足です。__o_

更に別解

elem.attachEvent('onclick', new Function("", "alert('いぬ No.'+"+i+");"));

Javascript では関数というのは単なるオブジェクトであるということ、
function(){} という書き方は Function オブジェクトを new するためのリテラルでしかないというのがポイント。

ていうかこっちの方が簡潔やね

>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でない変数にはアクセスできない”
とコンパイラに怒られました。

JavaScriptに引数の省略機能はないのでしょうか?もしあればそれを使って簡潔な表記ができそうだと思いました。説明のためにコードを書いたらたくさん書きすぎてしまったので詳しい内容はリンク先をご覧ください。
http://www.nishiohirokazu.org/blog/2006/05/re.html

コメントを投稿


画像の中に見える文字を入力してください。