闭包不过如此

作者: 浪里行舟 | 来源:发表于2018-03-24 07:04 被阅读243次

    引子

    闭包,是 javascript 中很重要的一个概念,几乎每次面试都会被问及,然而却是晦涩难懂,令面试者无所适从。本文尽可能用简单易懂的话,讲清楚闭包的概念及其常见的面试题。
    我们先来看一个例子:

         function outer(){
              var a = 888;
                function inner(){
                    console.log(a);
                }
            }
            inner();//报错
    

    inner这个函数不能在outer外面调用,因为outer外面没有inner的定义,然而如果我非要让inner函数在作用域外执行,该怎么修改上述代码?


    本文框架图

    闭包是什么

    我们可以对上面代码进行如下修改:

       function outer(){
        var a = 888;
        function inner(){
        console.log(a);
        }
        return inner;   //outer返回了inner的引用
        }
        var inn = outer();  //inn就是inner函数了
        inn(); //执行inn,全局作用域下没有a的定义,
             //但是函数闭包,能够把定义函数的时候的作用域一起记住,输出888          
    

    在outer()执行之后,其返回值也就是内部的inner函数赋值给变量inn并调用了inn(),实际上就是通过不同的标识符引用调用了内部函数inner。调用inn()的时候,注意这个调用地点,我们是在outer函数外部调用的inn(),也就是说,在调用的这个地方,根本就不存在a变量的作用域。
    但是神奇的是,控制台居然输出了数字1。这就说明了,inner函数能够持久保存自己定义时的所处环境,并且即使自己在其他的环境被调用的时候,依然可以访问自己定义时所处环境的值。

    那到底什么是闭包呢?

    当函数可以记住并访问所在的词法作用域,即使函数是在当前词法作用域之外执行,这就产生了闭包。 ----《你不知道的Javascript上卷》
    我个人理解,闭包就是函数中的函数(其他语言不能函数再套函数),里面的函数可以访问外面函数的变量,外面的变量的是这个内部函数的一部分。

    闭包的特性

    每个函数都是闭包,每个函数天生都能够记忆自己定义时所处的作用域环境。把一个函数从它定义的那个作用域,挪走,运行。这个函数居然能够记忆住定义时的那个作用域。不管函数走到哪里,定义时的作用域就带到了哪里。接下来我们用两个例子来说明这个问题:

    //例题1
    var inner;
    function outer(){
    var a=250;
    inner=function(){
    alert(a);//这个函数虽然在外面执行,但能够记忆住定义时的那个作用域,a是250
      }
    }
    outer();
    var a=300;
    inner();//一个函数在执行的时候,找闭包里面的变量,不会理会当前作用域。
    
    //例题2
    function outer(x){
      function inner(y){
      console.log(x+y);
      }
    return inner;
    }
    var inn=outer(3);//数字3传入outer函数后,inner函数中x便会记住这个值
    inn(5);//当inner函数再传入5的时候,只会对y赋值,所以最后弹出8
    

    闭包的内存泄漏

    栈内存提供一个执行环境,即作用域,包括全局作用域和私有作用域,那他们什么时候释放内存的?

    • 全局作用域----只有当页面关闭的时候全局作用域才会销毁
    • 私有的作用域(只有函数执行会产生私有的作用域)
      一般情况下,函数执行会形成一个新的私有的作用域,当私有作用域中的代码执行完成后,我们当前作用域都会主动的进行释放和销毁。但当遇到函数执行返回了一个引用数据类型的值,并且在函数的外面被一个其他的东西给接收了,这种情况下一般形成的私有作用域都不会销毁。如下面这种情况:
    function fn(){
    var num=100;
    return function(){
      }
    }
    var f=fn();//fn执行形成的这个私有的作用域就不能再销毁了
    

    也就是像上面这段代码,fn函数内部的私有作用域会被一直占用的,发生了内存泄漏。所谓内存泄漏指任何对象在您不再拥有或需要它之后仍然存在。闭包不能滥用,否则会导致内存泄露,影响网页的性能。闭包使用完了后,要立即释放资源,将引用变量指向null。接下来我们看下有关于内存泄漏的一道经典面试题:

     <script>  
      function  outer(){  
      var num=0;//内部变量  
      return  function add(){//通过return返回add函数,就可以在outer函数外访问了。  
      num++;//内部函数有引用,作为add函数的一部分了  
      console.log(num);  
      };  
     }  
      var func1=outer();//  
      func1();//实际上是调用add函数, 输出1  
      func1();//输出2  因为outer函数内部的私有作用域会一直被占用
      var func2=outer();  
      func2();// 输出1  每次重新引用函数的时候,闭包是全新的。
      func2();// 输出2  
     </script>  
    

    闭包的作用

    1.使用闭包可以访问函数中的变量。
    2.可以使变量长期保存在内存中,生命周期比较长。
    3.闭包的函数另外的意义:可以用来实现模块化。请看下面的例子:

    var common=(function(){
    var name="通用模块";
    function initPage(){
    alert(name);
    return {
    initPage2:initPage
    })();
    common.initPage2();//通用模块
    

    相关文章

      网友评论

      本文标题:闭包不过如此

      本文链接:https://www.haomeiwen.com/subject/eimiqftx.html