• 闭包的原理,优缺点,应用场景,常见面试题总结


    闭包的原理,优缺点,应用场景,常见面试题总结

    1.概念

    闭包:可以把闭包理解成一个函数,一个父函数里面嵌套的子函数(也就是函数中的函数),且该子函数必须使用了父函数的变量。

    如:

     function f1(){
           var b=2;
           function f2(){
               b++;
               console.log(b);
           }
           return f2;
        };
        var f=f1();
        f();
    

    在上面代码中,f1()是父函数,而f2()是f1()的子函数,且f2中使用了父函数中的变量b。在这里,f2就是闭包。

    闭包形成条件:

    1. 必须有一个内嵌函数

    2. 内嵌函数必须引用外部函数中的变量

    3. 外部函数的返回值必须是内嵌函数

    2.生命周期

    产生:在嵌套的子函数定义执行完成时就产生了

    死亡:在嵌套的内部函数成为垃圾对象时

     function f1(){
            //此时就已经产生闭包(因为函数提升)
           var b=2;
           function fn(){
               b++;
               console.log(b);
           }
           return fn;
        };
        var f=f1();
        f();
        f=null//闭包消失,因为内部函数成为了垃圾对象(没有变量在引用它)
    

    3.优缺点

    优点:

    (1)可以让子函数内的变量被访问

    (2)可以让子函数的变量的值保留在内存里面不被清除

    (3)方便调用上下文的局部变量

    (4)加强封装性

    缺点:

    (1)由于闭包会让子函数的变量的值保留在内存里面,就容易造成大量内存被占用,内存消耗过大,可能会导致内存泄漏,影响网页性能。解决方法:及时清除不使用的局部变量,也就是赋值null。

    (2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。

    4.常见面试题

    (1)

    function fun(n,o) {
        console.log(o);
            return {
                fun:function(m) {
                    return fun(m,n);
                }
            };
    }
    var a = fun(0); a.fun(1);  a.fun(2);  a.fun(3);  //undefined,0,0,0
    var b = fun(0).fun(1).fun(2).fun(3);             //undefined,0,1,2
    var c = fun(0).fun(1);  c.fun(2);  c.fun(3);     //undefined,0,1,1
    

     第一行:fun(0)即fun(0,o),o并未赋值->undefined,  a其实是function()函数,也就是a是一个闭包,a=fun(0)返回的是fun(m,0),所以后面的fun(0); a.fun(1); a.fun(2); a.fun(3)都为0,因为闭包中的n没有变,都是同一个闭包a。

    第二行:

    同样,fun(0)即fun(0,o),o并未赋值->undefined,    fun(0).fun(1)->fun(1,0)这时输出0,fun(0).fun(1).fun(2)->fun(0,1).fun(2)->fun(2,1)这时输出1,

    fun(0).fun(1).fun(2).fun(3)->fun(2,1).fun(3)->fun(3,2),这时输出2

    第三行:由于后面两个c.fun(2); c.fun(3)都是同一个闭包c在起作用,所以后面都是1

    (2)

    function f1(){
        var n=999;
        nAdd=function(){n+=1}
        function f2(){
          alert(n);
        }
        return f2;
      }
      var result=f1();
      result(); // 999
      nAdd();
      result(); // 1000
    

    result是f2(),所以调用result(),就是执行f2,由于全局变量n为999,所以这里就是999。

    nAdd()这里执行了一遍函数,n变成了1000。

    再执行一遍result(),此时n已经是1000了

    (3)

       function f1() {
            var n = 999;
            nAdd = function () {
              n += 1;
            };
            function f2() {
              alert(n);
            }
            return f2;
          }
          var result1 = f1();
          var result2 = f1();
          result1(); // 999
          result2(); //999
          nAdd();
          result1(); // 是999而不是1000,这是为何呢?
          result2(); //1000
    

    var result1 = f1();

    var result2 = f1();
    这两行调用了两次f1(),相当于分别执行了以下两段代码,
    执行result1=f1()时:
     function f1(){
        var n=999;
        //n在result1中的引用为temp1
        var temp1 = n;
        nAdd=function(){
            temp1 += 1;
        };
        function f2(){
          alert(temp1);
        }
        return f2;
      }
    

     执行result2=f1()时:

    function f1(){
        var n=999;
         //n在result2中的引用为temp2
        var temp2 = n;
        nAdd=function(){
            temp2 += 1;
        };
        function f2(){
          alert(temp2);
        }
        return f2;
      }
    

     由于result1和result2分别形成了闭包,分别对n进行了保存,所以顺着顺序执行nAdd();时,这里是对result2闭包中的n进行了修改,result1闭包把它自己的n保护起来了。

    所以执行完nAdd(),result1()还是999,result2()变成1000。

    (4)

    function fn(){//创建父函数(爸爸)
       var arr = [];
       for(var i = 0;i < 5;i ++){//这里循环相当于创建了五个子函数(儿子)
    	 arr[i] = function(){
    		 return i;
    	 }
       }
       return arr;
    }
    var list = fn();//这里只调用了一次父函数,
    for(var i = 0,len = list.length;i < len ; i ++){
       console.log(list[i]());
    }  //5 5 5 5 5
    
     

    参考博客:

    https://blog.csdn.net/yingzizizizizizzz/article/details/72887161

    https://blog.csdn.net/weixin_43586120/article/details/89456183

    https://blog.csdn.net/yingzizizizizizzz/article/details/77726346

    https://blog.csdn.net/u011043843/article/details/46640847?utm_source=app&app_version=4.5.7

  • 相关阅读:
    springCloud学习6(Spring Cloud Sleuth 分布式跟踪)
    springCloud学习5(Spring-Cloud-Stream事件驱动)
    springCloud学习4(Zuul服务路由)
    springCloud学习3(Netflix Hystrix弹性客户端)
    springCloud学习笔记2(服务发现)
    EFCore
    webBrowser.DocumentText重新赋值无效解决方法
    C# SQLite写入和读取DateTime类型
    自定义协议链接 ------2.实践篇
    自定义协议链接 ------1.原理篇
  • 原文地址:https://www.cnblogs.com/bxtfdxg/p/14622460.html
Copyright © 2020-2023  润新知