我正在阅读本书的第4章,并在下面的代码中遇到了问题。这段代码正好在我脑海中,我似乎无法弄清楚为什么(i)在那里。他在书中解释说,但这并不是一个很好的解释。
//Make a function that assigns event handler functions to an array of nodes the right way
//When you click on a node, an alert box will display the ordinal of the node.
var add_the_handlers = function(nodes){
var i;
for (i = 0; i < nodes.length; i += 1){
nodes[i].onclick = function(i){
return function (e){
alert(i);
};
}(i); // <-------------this i right here.
}
};
答案 0 :(得分:5)
在函数表达式后面放置(someValue)
将立即调用该函数。
foo = function (i){ ... }(i);
会给你相同的结果:
bar = function (i){ ... };
foo = bar(i);
通过在该函数内部(通过i
定义)具有本地范围的变量function (i)
,该值被捕获(关闭)并且在另一个i
时不会发生变化(函数外部的范围内)会发生变化(每次循环时都会发生变化)。
我通常在闭包内使用不同的名称作为变量。重复的变量名称可能令人困惑。
nodes[i].onclick = function(closed_over_i){
return function (e){
alert(closed_over_i);
};
}(i);
答案 1 :(得分:1)
(i)
立即调用第一个匿名函数,这意味着nodes[i].onclick
获得第二个匿名函数。
实际上有两个i
可能会使问题混淆一点 - add_the_handlers
函数范围内有一个,onclick
函数中有另一个。 / p>
我会重写它并对行进行编号以帮助解释:
1. var add_the_handlers = function (nodes) {
2. var i;
3. for (i = 0; i < nodes.length; i += 1){
4. nodes[i].onclick = function (j) {
5. return function (e) {
6. alert(j);
7. };
8. }(i);
9. }
10. };
这在功能上与原始代码完全相同;因为它们的范围不同,所以你的版本可以有相同的变量名,而不会相互干扰;它只是一个新变量i
,只能在该函数中使用。我已将其更改为j
只是为了使两个范围更清晰。
在第4行,你定义了一个匿名函数,它接受一个参数j
,然后在第8行立即用当前值i
调用它。您这样做是为了创建一个新范围,将i
的当前值复制到其中。 j
的值不再与循环增加的i
相关联。
这意味着您现在可以在该新范围中的第5行(闭包)上定义第二个匿名函数,因此j
将始终引用该范围中的变量。然后返回该函数并将其分配给nodes[i].onclick
,因此当它被调用时,它将始终警告i
的值,即使它被定义,甚至认为它现在在不同的范围内调用
如果没有第8行的函数调用,示例第6行的i
将位于从第1行开始的外部函数的范围内;因为对于数组中的每个项目,在第3行的循环中更改了该值,所有onclick
函数都会提醒i
,nodes.length
的最终值。
示例代码只是一种更紧凑和令人困惑的写作方式:
var create_closure = function (val) {
// This val is in a separate scope to the i
return function (e) {
// This is the closure; it references a variable in the parent scope
alert(val);
};
};
var add_the_handlers = function (nodes){
var i;
for (i = 0; i < nodes.length; i += 1) {
nodes[i].onclick = create_closure(i);
}
};
虽然这个例子很重要,但一般来说这是编写解决方案的更好方法 - 较少的嵌套和命名函数使得之后出现的任何人都更清楚,并且你不太可能犯错误后来。至少在闭包中使用不同的变量名。