JavaScript(8)— 闭包

  • 2020 年 3 月 26 日
  • 筆記

JavaScript(8)— 闭包

理解闭包 我的理解是:闭包就是能够读取其他函数内部变量的函数。由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以简单这样理解

"函数A中,有一个函数B,函数B中可以访问函数A中定义的变量或者是数据,此时形成了闭包"。所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁

一、闭包的由来

1、函数嵌套函数

JS之所以会有闭包,是因为JS不同于其他规范的语言,JS允许一个函数中再嵌套子函数,正是因为这种允许函数嵌套,导致JS出现了所谓闭包。

function a(){     var a=1;      function b(){        alert(a);      };      b();  }  a();  

在JS正常的函数嵌套中,父函数a调用时,嵌套的子函数b的结构,在内存中产生,然后子函数又接着调用了,子函数b就注销了,此时父函数a也就执行到尾,父函数a也会把

自己函数体内调用时生成的数据从内存都注销。

function a(){      var a=1;      function b(){         alert(a);      }      return b;  }  var f=a();  

这个例子中,父函数调用时,函数体内创建了子函数b,但是子函数并没有立即调用,而是返回了函数指针,以备“日后再调用”,因为“准备日后调用”,此时父函数a执行完了

也不敢注销自己的作用域中的数据,因为一旦注销了,子函数b日后再调用时,沿着函数作用域链往上访问数据,就没有数据可以访问了,这就违背了JS函数作用域链的机制。

正因此,子函数要“日后调用”,导致父函数要维持函数作用域链,而不敢注销自己的作用域,那么这个子函数就是“闭包函数”。

2、JS变量的作用域

理解闭包还要理解一个知识点就是 JavaScript的变量作用域。与大多数语言相同,JavaScript变量的作用域分为全局变量和局部变量。**函数内部可以访问全局变量,但是

函数外部无法访问函数内部的局部变量**

示例

function f1(){    let  n =100;    var m=99;    console.log(n);    console.log(m);  }  f1();   //输出:100 , 99  console.log(n); //输出:undefined  console.log(m);  //输出:undefined  

注意 在函数内部声明变量的时候一定要用let或者var。否则,实际上声明了一个全局变量

思考 函数外部如何读取局部变量?

要在函数外部读取局部变量,可以通过在函数内部再定义一个函数的方法来实现。

示例

function f1(){    var n =100;    function f2(){      console.log(n);    }    return f2;  }  let result =f1();  result();  //输出100  

在上面的代码中,函数f2就被包括在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。但是反过来就不行,f2内部的局部变量,对f1就是不可见的。这就是Javascript

语言特有的"链式作用域"结构,子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。

二、闭包的作用

闭包可以用在许多地方。它的最大用处有两个 1、可以读取函数内部的变量。2、让变量的值始终保持在内存中。

第一个上面已经实现了,这里就不再重复说明。

1、让变量的值始终保持在内存中

一般来说,全局变量的生存周期是永久的,直到我们主动销毁。而在函数内的局部变量来说,当退出函数时,这些函数变量立即失去它们的价值,也就被垃圾回收机制

销毁了,也算寿终正寝。

示例

    //普通的函数      function f1() {        var num = 10;        num++;        return num;      }      console.log(f1()); //11      console.log(f1()); //11      console.log(f1()); //11  

可是在闭包中,却不是这样。它可以让这些变量的值使用保持在内存中(不被垃圾回收)

示例

    //函数模式的闭包      function f2() {        var num = 10;        function f3() {          num++;          return num;        }        return f3;      }      var ff = f2();      console.log(ff());//11      console.log(ff());//12      console.log(ff());//13  

由此可见,当退出函数后,局部变量 num 并没有立即消失,一直存在,这样在第二次调用时 num 才会是在 11的基础上加1,是12,以后每次调用也才会不断加1。

思考 为什么会这样呢?

原因就在于f2是f3的父函数,而f3被赋给了一个全局变量,这导致f3始终在内存中,而f3的存在依赖于f2,因此f2也始终在内存中,不会在调用结束后,被垃圾回收机制

(garbage collection)回收。

三、示例

为了更好理解闭包在实际开发中的应用,这里举几个简单例子来说明闭包。

1、产生三个随机数,但是都是相同的

代码

  <script>      //非闭包      function showRandom() {        var num=parseInt(Math.random()*10+1);        console.log(num);      }        showRandom(); //随机      showRandom(); //随机      showRandom(); //随机      console.log("===========================");      //闭包的方式,产生三个随机数,但是都是相同的      function f1() {        //这个只执行一次        var num=parseInt(Math.random()*10+1);        return function () {          console.log(num);        }      }      var ff=f1();      ff(); //这里三个值都是一样的      ff();      ff();    </script>  

运行结果

2、点赞示例

先展示运行结果

代码

<!DOCTYPE html>  <html lang="en">  <head>    <meta charset="UTF-8">    <title>点赞应用</title>    <style>      ul {        list-style-type: none;      }      li {        float: left;        margin-left: 10px;      }        img {        width: 200px;        height: 180px;      }        input {        margin-left: 30%;      }    </style>  </head>  <body>  <ul>    <li><img src="images/ly.jpg" alt=""><br/><input type="button" value="赞(1)"></li>    <li><img src="images/lyml.jpg" alt=""><br/><input type="button" value="赞(1)"></li>    <li><img src="images/fj.jpg" alt=""><br/><input type="button" value="赞(1)"></li>    <li><img src="images/bd.jpg" alt=""><br/><input type="button" value="赞(1)"></li>  </ul>  <script>      //获取所有的按钮    //根据标签名字获取元素    function my$(tagName) {      return document.getElementsByTagName(tagName);    }    //闭包缓存数据    function getValue() {      var value=2;      return function () {        //每一次点击的时候,都应该改变当前点击按钮的value值        this.value="赞("+(value++)+")";      }    }    //获取所有的按钮    var btnObjs=my$("input");    //循环遍历每个按钮,注册点击事件    for(var i=0;i<btnObjs.length;i++){      //注册事件      btnObjs[i].onclick=getValue();    }    </script>  </body>  </html>  

参考

1、js闭包的本质

2、JS闭包系列

3、JavaScript闭包

4、js 闭包

别人骂我胖,我会生气,因为我心里承认了我胖。别人说我矮,我就会觉得好笑,因为我心里知道我不可能矮。这就是我们为什么会对别人的攻击生气。  攻我盾者,乃我内心之矛(5)。