閉包的原理,優缺點,應用場景,常見面試題總結


閉包的原理,優缺點,應用場景,常見面試題總結

 

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM