1

部分的なアプリケーションを使用してデータをコールバックに渡す必要がありましたが、その後のループの実行によってコールバックに渡すデータが変更されないようにするために、間接レイヤーが必要であることがわかりました。

非常に単純な作業例については、こちらを参照してください: http://jsfiddle.net/s5gVj/

(jsfiddleが自明である場合は、以下の質問に直接スキップしてください)

間接性なしで

これにより、ラベルは常に「ボタン 1 が押されました」に設定されます。

これは、どのボタンが押されたかに関係なく発生します。

for(var i = 0 ; i < buttons.length ; i++)
{
    var buttonID = i;
    $(buttons[i]).click(   
        function(e)
        {
            $("label").html("Button " + buttonID + " pressed.");
        });
}

関数を介して間接的に

一方、途中で関数を使用すると問題が修正されます

最初のボタンの場合、結果は「ボタン 0 が押されました」になります。

2番目のボタンの場合、「ボタン1が押されました」になります。

var buttonClickHandlerWithID = 
    function(buttonID)
    {
        return function(e)
        {
            $("label").html("Button " + buttonID + " pressed.");
        }
    }

for(var i = 0 ; i < buttons.length ; i++)
{
    $(buttons[i]).click(buttonClickHandlerWithID(i));
}


なぜこれが起こるのですか?変数がコピーされ、同じデータへの参照がなくなることを保証する関数呼び出しに何か特別なことがありますか?それとも何か他のことが起こっていますか?

for ループ内で宣言された変数が各反復で再作成されることを期待していたので、各反復は個別に行われますが、そうではないと思いますか?

4

2 に答える 2

2

JavaScript には関数スコープしかありません。関数内のすべてのvar宣言 (もちろんネストされた関数内を除く) は、関数の先頭にあるかのように扱われます。ネストされた{ ... }ブロックには独自のスコープがありません。これは、C++ や Java などとの大きな違いです。

于 2014-06-30T16:10:07.253 に答える
1

Pointy が答えたように、javascript には関数スコープしかありません。これはi、コード内でループの外側にスコープが設定されていることを意味し、後で (クリックして) 参照すると、forループが終了した後の状態になります。

.each()の代わりにjquery を使用するforと、関数をコールバックとして受け取るため、ローカル スコープが取得されます。

var i = 0;
var directButtons = $("button.direct");

// jquerys each method takes a function callback
directButtons.each(function () {
    i++;
    // since this is inside a function
    var buttonID = i; // buttonID will be locally scoped

    $(this).click(function (e) {
        // this inner function "close over" (google "javascript closure") and
        // remembers the value of buttonID in the .each() loop
        $("label").html("Button " + buttonID + " pressed. " +
          "'i' will be whatever it ended up after the .each() is finished: " + i);
    });
});

http://jsfiddle.net/s5gVj/3/

fixthesejquery.comのスライド 55 もご覧ください。


これは、すぐに呼び出される関数式を使用して解決することもできます(「クロージャーによる状態の保存」というタイトルの部分を参照してください)。

var directButtons = $("button.direct");

for(var i = 0 ; i < directButtons.length ; i++){
    (function(buttonID){
        // buttonID is now scoped to this function
        $(directButtons[buttonID]).click(function (e) {
            $("label.direct").html("Button " + buttonID + " pressed. , 'i' is always " + i);
        });
    })(i); // call the function immediately, supply the value of "i" as parameter
}

http://jsfiddle.net/s5gVj/4/

于 2014-06-30T16:29:12.427 に答える