zoukankan      html  css  js  c++  java
  • JavaScript(8)--- 闭包

    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 {
           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)。
    
  • 相关阅读:
    删除 Visual studio 生成后的临时文件
    C# 中的委托和事件(转)
    C#复制DataRow出现“该行已经属于此表”错误的解决办法(转)
    ini配置文件读取类
    c# wpf窗体前端显示问题
    注册系统热键类(原创)
    C# 窗体最小化的托盘/系统通知区域(转)
    php explode()返回值
    $_SERVER['SCRIPT_NAME']
    svn合并初次使用心得
  • 原文地址:https://www.cnblogs.com/qdhxhz/p/12293393.html
Copyright © 2011-2022 走看看