3

この解決策は機能しますが、2 番目の "return function()" が何をするのかわかりませんか?

for (var i = 0; i < photos.length; i ++) {
    img.onclick = (function(photo) {
        return function() {
            hotLink(photo); //window.location = '/pics/user/' + photo.user_id;  
        };  
    })(photos[i]);

また、なぜ (photos[i]); を含める必要があるのですか? 最後に?

以前はこれがあり、onclick は常に最後の写真 [i] にリンクしていました。

  for (var i = 0; i < photos.length; i ++) {
      img.onclick = function() {
          window.location = 'pics/user/' + photo.user_id
      };
  }
4

3 に答える 3

1

関数呼び出しは、JavaScript で新しい変数スコープを作成する唯一の方法だからです。

したがってphotos[i]、その関数に渡すと、その呼び出しのスコープに対してローカルになります。

次に、同じスコープでハンドラー関数も作成するため、ハンドラーはその特定の を参照しますphoto

最終的に、ループが 10 回繰り返されると、10 個の関数が呼び出され、10 個の新しい個別の変数スコープが作成されます。この変数スコープは、それぞれの変数を参照photoし、それぞれの個別のスコープからハンドラーを作成して返します。


そのような関数をインライン化しないと、これらのことがより明確になることがあります。

for (var i = 0; i < photos.length; i ++) {
    img.onclick = createHandler(photos[i]); // pass individual photos to createHandler
}

function createHandler(photo) {
       // In here, the individual photo is referenced

       // And we create (and return) a function that works with the given photo
    return function() {
        hotLink(photo); //window.location = '/pics/user/' + photo.user_id;
    };
}

したがって、ループが 10 回繰り返される場合createHandler()、個々の写真を渡すたびに 10 回呼び出します。

関数呼び出しごとに変数スコープが作成され、各スコープ内にイベント ハンドラーが作成されるため、10 個の関数すべてが 10 個の変数スコープで作成され、それぞれが渡された写真を参照することになります。


反復ごとの関数呼び出しがないと、すべてのハンドラー関数が同じ変数スコープで作成されます。つまり、すべてのハンドラー関数が同じ変数を共有し、各ループ反復で上書きされる可能性があります。

于 2012-06-01T03:38:07.177 に答える
1

photo = photos[i]これを行うと(質問で省略したものがあると仮定して):

img.onclick = function() { window.location = 'pics/user/' + photo.user_id };

関数内の変数photoは、関数外と同じ変数を参照しphotoます。関数を定義した時点で変数の現在の値を取得するのはスナップショットではありません。これは、同じ変数への単なる参照です。周囲のループは、反復ごとにその変数の値を変更しますが、毎回新しい変数を作成するわけではありません。同じものを再利用しています。したがって、生成するすべての関数は、まったく同じ変数 (唯一無二) を参照しますphoto

誰かが実際に画像をクリックして関数を呼び出すまでに、ループは終了してからずっと経過photoしており、メイン プログラムのスコープからは外れていますが、これらのすべての関数がまだループを参照しているため、ループはまだメモリ内にあります。そして、それが最後に割り当てられたものであるため、リストの最後の項目を指していることに気付くでしょう。

したがって、関数が作成されると変更されない独自の変数を各 onclick 関数に与える必要があります。Javascript でこれを行う方法は、ブロック スコープがないため、関数を呼び出して値をパラメーターとして渡すことです。関数内で宣言された関数パラメーターと変数 (関数内で使用されているが関数外で宣言photoされている上記の動作しない例とは対照的に) は、関数呼び出しごとに新しく作成されます。が関数パラメーターとして宣言されている場合、各 onclick は独自のコピーを取得し、それ以外には何も変更できないため、誰かが最終的に画像をクリックしたときに正しい値が保持されます。photo

静的関数ジェネレーター関数を使用した場合は、より明確になる可能性があります。インラインで宣言して呼び出すことを行う理由はまったくありません。ループの外側で、これを一度宣言できます。

function makeOnclick(somePhoto) {
    return function() { hotlink(somePhoto); }
}

そして、ループ本体はこれを行うことができます:

img.onclick = makeOnclick(photo)

それを呼び出してパラメーターとしてmakeOnclick渡します。関数は遠く離れた場所で宣言されており、直接使用したくても使用できませphotoん。その変数はまったく見えません。代わりに、ローカル パラメータだけがあり、これは を呼び出すたびに新しい変数として作成されます。呼び出しの時点での値で初期化されますが、これは単なるコピーであるため、次のループ反復で変更されても、 の特定のインスタンスは同じままです。次の反復で が呼び出されると、 の新しい値に初期化された の新しいインスタンスが作成され、以降も同様です。したがって、返される内部関数が継承されているにもかかわらず、makeOnclickphotosomePhotomakeOnclickphotophotosomePhotomakeOnclicksomePhotophotomakeOnClicksomePhotovar、その var はmakeOnClick;のインスタンス用に特別に作成されたものです。これらの返された関数はすべて、独自の private を取得しsomePhotoます。

上記の作業コードは、わずかに異なる方法でまったく同じことを行っています。ループの外側で関数を一度宣言して何度も呼び出す代わりにmakeOnclick、ループを介して毎回匿名関数として再宣言し、すぐに呼び出します。このコード:

img.onclick = (function(x) { blah(x); })(photo);

これと同じです:

function foo(x) { blah(x); }
img.onclick = foo(photo);

関数に名前を付ける必要はありません。一般的な JavaScript では、次のようになります。

(function (x,y,z) { doSomething(x,y,z); })(a,b,c);

これと同じです:

function callDoSomething(x,y,z) { doSomething(x,y,z); }
callDoSomething(a,b,c);

ただし、関数には名前がなく、どこにも保存されません。呼び出された直後に消えます。

そのため、ループを通過するたびに onclick-generator 関数を宣言し、すぐに一度に呼び出すのは適切で簡潔ですが、それほど効率的ではありません。

于 2012-06-01T03:38:21.633 に答える
1

返される関数はクロージャーです。そのようにループしているときiは、最後の画像で立ち往生しているループの終わりまで、各ループで更新されます。自己実行関数を追加しphoto[i]てそれを渡すと、返された関数内に現在の値が として永久に含まれphotoます。

クロージャの詳細は次のとおりです: JavaScript クロージャはどのように機能しますか?

現在の問題の詳細については、こちら:ループ内の JavaScript クロージャー – 簡単な実用例

于 2012-06-01T03:39:49.443 に答える