閉包的原理,優缺點,應用場景,常見面試題總結
閉包的原理,優缺點,應用場景,常見面試題總結
1.概念
閉包:可以把閉包理解成一個函式,一個父函式裡面巢狀的子函式(也就是函式中的函式),且該子函式必須使用了父函式的變數。
如:
function f1(){ var b=2; function f2(){ b++; console.log(b); } return f2; }; var f=f1(); f();
在上面程式碼中,f1()是父函式,而f2()是f1()的子函式,且f2中使用了父函式中的變數b。在這裡,f2就是閉包。
閉包形成條件:
-
必須有一個內嵌函式
-
內嵌函式必須引用外部函式中的變數
-
外部函式的返回值必須是內嵌函式
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
varresult1=f1();
varresult2=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