这个解决方案有效,但我不明白第二个“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]);
另外,为什么我必须包括(照片[i]);在末尾?
之前,我有这个,并且onclick将始终链接到最后一张照片[i]。
for (var i = 0; i < photos.length; i ++) {
img.onclick = function() {
window.location = 'pics/user/' + photo.user_id
};
}
答案 0 :(得分:1)
因为函数调用是在JavaScript中创建新变量作用域的唯一方法。
因此,您将photos[i]
传递给该函数,并且它变为该调用范围的本地。
然后,您还在同一范围内创建处理函数,因此处理程序引用该特定photo
。
所以最后,如果循环迭代10次,你将调用10个函数,创建10个新的独立变量作用域,引用每个单独的photo
并从每个单独的作用域创建并返回处理程序。 / p>
如果你没有内联这样的功能,这些事情有时会更清楚。
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个函数,每个作用都引用了传递的照片。
如果没有per-iteration函数调用,所有处理函数都在相同的变量作用域中创建,这意味着它们共享相同的变量,这些变量很可能在每次循环迭代中被覆盖。 / p>
答案 1 :(得分: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
返回的内部函数继承了somePhoto
var,该var也只是为makeOnClick
的实例创建的;每个返回的函数都有自己的私有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函数并立即立即调用它既简洁又简洁,但效率不高。
答案 2 :(得分:1)
返回的函数是一个闭包。当你循环时,i
正在每个循环上更新,直到循环结束你最后一个图像。添加自执行函数并在其中传递photo[i]
将永久将当前值包含在返回的函数中photo
。
以下是有关闭包的更多信息:How do JavaScript closures work?
此处了解有关您当前问题的更多信息:JavaScript closure inside loops – simple practical example