闭包:"Javascript:Good Parts"示例的逐行说明?

Mat*_*rym 11 javascript methods closures function

我正在阅读"Javascript:The Good Parts",我对这里发生的事情感到非常困惑.将非常感谢更详细和/或简化的解释.

// BAD EXAMPLE

// Make a function that assigns event handler functions to an array  of nodes the wrong way.
// When you click on a node, an alert box is supposed to display the ordinal of the node.
// But it always displays the number of nodes instead.

var add_the_handlers = function (nodes) {
    var i;
    for (i = 0; i < nodes.length; i += 1) {
        nodes[i].onclick = function (e) {
            alert(i);
        }
    }
};

// END BAD EXAMPLE
Run Code Online (Sandbox Code Playgroud)

add_the_handlers函数旨在为每个处理程序提供唯一的编号(i).它失败是因为处理函数绑定到变量i,而不是函数生成时变量的值i:

// BETTER EXAMPLE

// 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);
    }
};
Run Code Online (Sandbox Code Playgroud)

现在,我们不是为onclick分配函数,而是定义一个函数并立即调用它,传入i.该函数将返回一个事件处理函数,该函数绑定到i传入的值,而不是已i定义的值add_the_handlers.返回的函数被分配给onclick.

Dan*_*llo 20

我认为这是JavaScript新手的一个非常常见的混淆源.首先,我建议查看以下Mozilla Dev文章,简要介绍闭包和词法范围的主题:

让我们从坏的开始:

var add_the_handlers = function (nodes) {
// Variable i is declared in the local scope of the add_the_handlers() 
// function. 
   var i;

// Nothing special here. A normal for loop.
   for (i = 0; i < nodes.length; i += 1) {

// Now we are going to assign an anonymous function to the onclick property.
       nodes[i].onclick = function (e) {

// The problem here is that this anonymous function has become a closure. It 
// will be sharing the same local variable environment as the add_the_handlers()
// function. Therefore when the callback is called, the i variable will contain 
// the last value it had when add_the_handlers() last returned. 
           alert(i);
       }
   }

// The for loop ends, and i === nodes.length. The add_the_handlers() maintains
// the value of i even after it returns. This is why when the callback
// function is invoked, it will always alert the value of nodes.length.
};
Run Code Online (Sandbox Code Playgroud)

正如Crockford在"好例子"中所建议的那样,我们可以用更多的闭包来解决这个问题.闭包是一种特殊的对象,它结合了两个东西:一个函数,以及创建该函数的环境.在JavaScript中,闭包的环境包含在创建闭包时在范围内的任何局部变量:

 // Now we are creating an anonymous closure that creates its own local 
 // environment. I renamed the parameter variable x to make it more clear.
 nodes[i].onclick = function (x) {

     // Variable x will be initialized when this function is called.

     // Return the event callback function.
     return function (e) {
         // We use the local variable from the closure environment, and not the 
         // one held in the scope of the outer function add_the_handlers().
         alert(x);
     };
 }(i); // We invoke the function immediately to initialize its internal 
       // environment that will be captured in the closure, and to receive
       // the callback function which we need to assign to the onclick.
Run Code Online (Sandbox Code Playgroud)

封闭函数不是让回调都共享一个环境,而是为每个回调函数创建一个新的环境.我们也可以使用函数工厂来创建一个闭包,如下例所示:

function makeOnClickCallback (x) {
   return function (e) {
      alert(x);
   };
}

for (i = 0; i < nodes.length; i += 1) {
   nodes[i].onclick = makeOnClickCallback(i);
}
Run Code Online (Sandbox Code Playgroud)