3

这个解决方案有效,但我不明白第二个“返回函数()”是做什么的?

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上面的非工作示例相反,它在函数内部使用但在函数外部声明)在每次函数调用时重新创建。当photo被声明为函数参数时,每个 onclick 都会获得自己的副本,其他任何东西都无法修改,因此当有人最终单击图像时,它仍然具有正确的值。

如果它使用静态函数生成器函数可能会更清楚;真的没有理由做内联声明和调用的事情。您可以在循环外声明一次:

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

然后循环体可以这样做:

img.onclick = makeOnclick(photo)

您正在调用makeOnclick并将其photo作为参数传递。该makeOnclick函数在很远的地方声明,photo即使您想要它也无法直接使用;它根本看不到那个变量。相反,它所拥有的只是它的本地参数somePhoto——每次调用时都会将其创建为一个全新的变量makeOnclick。它在调用时使用 的值初始化photo,但它只是一个副本,因此当photo在下一次循环迭代中发生更改时,该特定实例somePhoto将保持不变。当下一次迭代调用makeOnclick时,它将创建一个somePhoto初始化为 的新值的新实例photo,依此类推。所以即使makeOnClick返回的内部函数是继承somePhotovar,该 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 回答