不完全了解此关闭的工作原理

Not fully understanding how this closure works

本文关键字:工作 了解 不完全      更新时间:2023-09-26

我摘自 JavaScript 闭包如何工作?

我很难理解闭包。

 <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;
        }
    };
})();

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

在回答你的问题之前,首先让我们讨论一下你的代码是如何工作的:

当你在另一个函数中定义一个函数时,它会创建一个clouser。在 clouser 内部,内部函数可以访问外部函数范围,我的意思是外部函数的变量参数即使外部函数已返回。在您的代码中,外部函数是一个立即调用的函数。这意味着它在定义后立即被调用。当它返回时,内部函数被分配给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 中,即使外部函数返回,内部函数也可以访问外部函数的变量。因此,内部函数没有任何计数变量。它只是访问外部作用域的变量。因此,第三次单击后,它将再次分配给零。

 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>

如果这样声明:

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

您是对的,每次单击count都会初始化为 0。

但是,它是这样声明的:

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

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

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

因此,count单击事件期间不会初始化为 0。

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

正如您所说,您的函数成为 onclick 处理程序,并且计数的分配者仅执行一次(当您的函数创建并分配给单击处理程序时)。计数仍在函数的范围内,即函数保留一个引用并使用该引用。因此,它不会重置和递增。

我喜欢

思考的方式:

        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);

你声明一个立即调用的函数,语法如下:

(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 变量全局化来实现类似的功能,但这种模式的好处是它限制了全局变量,并且还为变量提供了隐私。来自立即调用函数外部的任何调用都无法访问计数。

外部函数(括在括号中的函数)是立即调用的函数表达式的示例。该函数同时被定义和调用,其返回值是分配给element.onclick的值。

那么,当我们可以直接将element.onclick设置为内部函数时,为什么要为此烦恼呢?嗯,这与JavaScript范围规则有关。获取私有作用域的唯一方法是将某些内容包装在函数中。在这里,count在外部函数中声明和初始化,有效地使其成为私有的。内部函数仍然可以访问和操作它。这就是术语闭包的全部含义 - 一个内部函数访问其封闭函数的变量。

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