1. ホーム
  2. ジャバスクリプト

[解決済み】Javascriptのinfamous Loop問題?重複

2022-04-07 09:48:05

質問

次のようなコード・スニペットを持っています。

function addLinks () {
    for (var i=0, link; i<5; i++) {
        link = document.createElement("a");
        link.innerHTML = "Link " + i;
        link.onclick = function () {
            alert(i);
        };
        document.body.appendChild(link);
    }
}

上記のコードは、5つのリンクを生成し、各リンクに現在のリンクIDを表示するアラートイベントをバインドするものです。しかし、これはうまくいきません。生成されたリンクをクリックすると、すべて "link 5" と表示されます。

しかし、次のコード・スニペットは期待通りに動作します。

function addLinks () {
    for (var i=0, link; i<5; i++) {
        link = document.createElement("a");
        link.innerHTML = "Link " + i;
        link.onclick = function (num) {
            return function () {
                alert(num);
            };
        }(i);
        document.body.appendChild(link);
    }
}

上記2つのスニペットは、以下のサイトから引用しています。 ここで . 著者の説明では クロージャ が魔法をかける。

しかし、その仕組みとHow クロージャ は、すべて私の理解を超えています。なぜ前者はうまくいかず、後者はうまくいくのでしょうか?どなたか、このマジックについて詳しい説明をお願いします。

解決方法は?

引用元 は、最初の例の説明のため。

JavaScript のスコープはブロックレベルではなく関数レベルです。クロージャを作成すると、そのスコープを囲む関数のレキシカル環境に追加されるだけです。

ループが終了すると、関数レベルの変数iは値5となり、それが内部関数に「見える」ことになる。

2つ目の例では、繰り返しステップごとに、外側の関数リテラルは独自のスコープとローカル変数を持つ新しい関数オブジェクトに評価されます。 num の現在の値が設定される。 i . として num 関数オブジェクトは独立しているので、次の反復ステップで古い値が上書きされることはありません。

この方法は、各リンクに対して2つの新しい関数オブジェクトを作成しなければならないので、かなり非効率的であることに留意してください。DOM ノードを情報の保存に利用すれば、簡単に共有することができるので、これは不要です。

function linkListener() {
    alert(this.i);
}

function addLinks () {
    for(var i = 0; i < 5; ++i) {
        var link = document.createElement('a');
        link.appendChild(document.createTextNode('Link ' + i));
        link.i = i;
        link.onclick = linkListener;
        document.body.appendChild(link);
    }
}