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

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

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

原文地址:https://www.cnblogs.com/bxtfdxg/p/14622460.html