关闭(好书的方式)

时间:2014-07-22 13:30:20

标签: javascript closures

我正在阅读本书的第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.
        }
    };

2 个答案:

答案 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函数都会提醒inodes.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);
    }
};

虽然这个例子很重要,但一般来说这是编写解决方案的更好方法 - 较少的嵌套和命名函数使得之后出现的任何人都更清楚,并且你不太可能犯错误后来。至少在闭包中使用不同的变量名。