没有完全理解这个闭包的工作原理

时间:2015-01-29 21:40:18

标签: javascript function closures

我摘录自How do JavaScript closures work?

我很难理解闭包。

 <button type="button" id="bid">Click Me!</button> 
 <script>
 var element = document.getElementById('bid');
element.onclick = (function() {
    // init the count to 0
    var count = 0;

    return function(e) {  // <- This function becomes the onclick handler
        count++;          //    and will retain access to the above `count`

        if (count === 3) {
            // Do something every third time
            alert("Third time's the charm!");

            //Reset counter
            count = 0;
        }
    };
})();

如何在调用之间保存'count'值?不应该通过var = 0重置每次调用吗?

6 个答案:

答案 0 :(得分:1)

在回答您的问题之前,首先让我们讨论您的代码的工作原理:

当你在另一个函数中定义一个函数时,它会创建一个clouser.Inside一个clouser内部函数可以访问外部函数 scope ,我的意思是外部函数的 变量 参数 即使外部函数已返回 。在您的代码中,外部函数是immediately-invoked-function。这意味着它在定义后立即被调用。当它返回时,内部函数被分配给onclick事件。单击函数可以访问,并修改外部函数的变量(in this case count which is defined in the outer function),即使它返回。 我已经在你的代码中注释掉了。通过它,它会很清楚

首先,立即调用函数(function(){...})()将立即调用。它返回另一个函数。所剩下的是一个返回的函数,它将被分配给onclik处理程序。 因此,返回后将是

 var element = document.getElementById('bid');
    element.onclick =function(e) {  // <- This returned function has becomes the onclick handler
            count++;          //  it can access the outer function's count variable and modify it even if the outer function returns.

            if (count === 3) {
                // Do something every third time
                alert("Third time's the charm!");

                //Reset counter
                count = 0;  // it can also reset outer function's count variable to zero
            }
        };
  

如何在“调用”之间保存“计数”值?不应该重置   每次调用var = 0?

右。如果你一次又一次地使用这个代码 并立即调用函数 ,每次计数将从零开始。但是如果你 一次又一次地点击 相同的按钮。即使外部函数已经返回,内部函数也可以访问它的变量。所以每次单击按钮都会修改它们

首先点击按钮将打印1,因此计数现在为1。 第二次单击将再次修改它并将打印2依此类推。即使外部函数返回,clousers内部函数也可以访问外部函数的变量。因此,内部函数没有任何计数变量。它只是访问外部作用域的变量。因此,在第三次点击后,它将再次分配为零。<​​/ p>

 var element = document.getElementById('bid');
 var mydiv=document.getElementById('mydiv');

element.onclick = (function() {

// init the count to 0
var count = 0;

return function(e) {  // <- This function becomes the onclick handler
    count++; 
   mydiv.innerHTML+=count+'</br>';		//    and will retain access to the above `count`

    if (count === 3) {
        // Do something every third time
        mydiv.innerHTML +="Third time's the charm!</br>";

        //Reset counter
        count = 0;
    }
};
})();
 <button type="button" id="bid">keep clicking me </button> 
   <div id='mydiv'></div>

答案 1 :(得分:0)

如果声明如下:

element.onclick = function() {
  var count = 0;
};

...您确认每次点击都会将count初始化为0。

但是,它的声明如下:

element.onclick = (function() {
  var count = 0;
  return function(e) {
    count++;
    ...
  };
})();

在这种情况下,onclick处理程序已分配给函数的结果,即:

return function(e) {
  count++;
  ...
};

因此,在点击事件期间count

count是代码中第一个函数的本地函数。由于第二个函数在该函数内,因此count可用。

答案 2 :(得分:0)

正如你所说,你的函数变成了onclick处理程序,count的赋值只执行一次(当你的函数被创建并分配给click处理程序时)。计数仍然在函数的范围内,即函数保留引用并使用该引用。因此,它不会重置和递增。

答案 3 :(得分:0)

外部函数,括在括号中的函数,是Immediately Invoked Function Expression的一个例子。该函数既被定义又被立即调用,其返回值是分配给element.onclick的函数。

那么,为什么我们可以直接将element.onclick设置为内部函数呢?那么,这与JavaScript范围规则有关。获取私有作用域的唯一方法是在函数中包装一些东西。这里,count在外部函数中声明并初始化,实际上使其成为私有。内部函数仍然可以访问和操作它。这就是闭包一词的意思 - 一个内部函数访问其封闭函数的变量。

外部函数只被调用一次(立即),因此count只被初始化为0一次。但是变量仍然存在于内存中并且不会被垃圾收集,因为JavaScript足够聪明,知道内部函数仍然需要访问它。

答案 4 :(得分:0)

我喜欢思考的方式:

        var count = 0;//Belongs to the window 
        //object and I like to imagine a window object 
        //around it so that you can see 
        //that everything in javascript is enclosed!

        function() {count++};

我很确定您在理解上述代码时没有任何困难。现在想象如下:

          var count = 0; //Belongs to the window 

          function x() {
              var count = 0;//Private to function x
              (function () {
                  count++;
                  alert(count);//What will this output?
              })();
          };

          x();
          alert(count);//And this?

你会看到第一个输出1,第二个输出0,原因是:因为它们是不同的变量。他们彼此无关。这就是人们喜欢在javascript中制作闭包的原因。如果污染全局名称空间,则可以覆盖它。

          var count = 0; //Belongs to the window 


          function x() {
              var count = 0;
              (function () {
                  count++;
                  alert(count);
              })();
          };

          x();
          alert(count);
          var count= 1;//Global count has now been overwritten
          alert(count);

答案 5 :(得分:0)

使用以下语法声明一个立即调用的函数:

(function() {
    ...
})();

首次加载时只运行一次。

返回的代码:

return function(e) {  // <- This function becomes the onclick handler
    count++;          //    and will retain access to the above `count`

    if (count === 3) {
        // Do something every third time
        alert("Third time's the charm!");

        //Reset counter
        count = 0;
    }
};

实际上是点击处理程序。因此,每次单击时只运行返回函数中的代码。

由于在Javascript中处理作用域的方式,内部函数可以访问外部函数中的变量。你可以通过使count变量全局来完成类似的功能,但是这个模式的好处是它限制了你的全局变量,并且还提供了变量隐私。来自您立即调用函数外部的调用将无法访问计数。