談談JS中的閉包
淺談閉包
如何產生閉包?
為了在函式外部訪問函式內區域性變數,就在父函式中定義一個子函式並讓它訪問了父函式的區域性變數,再通過父函式返回該子函式就實現了呼叫區域性變數的效果。
簡單舉例
function fun1() {
let num = 1;
return function fun2() {
console.log(num++);
}
}
const add = fun1();
add();
形成的條件
- 函式巢狀(函式作引數,函式作返回值)
- 內部函式訪問外部函式的區域性變數
可以把閉包理解成一個容器儲存了這些值(除錯時候看到的closure,裡面有key: value儲存閉包的值)
在案例中fun2這個函式訪問了父函式的num,函式銷燬前,num一直存在,我們每次呼叫fun2(),num值+1
閉包是什麼?
這是我從網上找到的一些的解釋。
閉包,不同於一般的函式,它允許一個函式在立即詞法作用域外呼叫時,仍可訪問非本地變數。 –維基百科
閉包就是能夠讀取其他函式內部變數的函式,閉包就是將函式內部和函式外部連線起來的一座橋樑。 –阮一峰
閉包就是對函式寫作時的環境引用。
閉包是通過JS回收機制保留某段作用域的一種手段。
兩點知識:
- Javascript函式內部可以直接讀取全域性變數
- 在JS中,變數查詢遵循就近原則,如果同級沒有該變數,則就一層一層向父級層查詢。—“鏈式作用域”
閉包與GC垃圾回收
JavaScript的垃圾回收機制,簡單來說就是:固定時間間隔,週期性的釋放不在使用的變數所佔記憶體。全域性變數的生命週期直至瀏覽器解除安裝頁面才會結束,區域性變數只在函式的執行過程中存在。
垃圾回收有兩個辦法:標記清除和引用計數(具體不懂)。
簡單點來說就是有沒有被引用,沒有被引用的就會被回收。
那麼在閉包中,函式呼叫了父函式作用域的值,然後被return出去了,這個函式中使用的外部值也就沒有被回收,直至函式銷燬。
function fun1() {
let num = 1;
return function fun2() {
console.log(num++ );
}
}
const add = fun1();
add();
結合上面的例子,父函式中的變數被子函式引用,子函式又被外部變數引用,可以說明閉包的產生和他們不被回收的原因。上面的列子中將全域性的add=null就可以回收fun2了。
閉包的優缺點
閉包的優點:可以在記憶體中維持一個變數,由於閉包,定義一個父函式中的變數只有通過子函式訪問到,無法通過其他途徑訪問修改,從而達到了保護變數安全的效果。
閉包的缺點:容易造成記憶體洩漏,所以不能濫用閉包。
閉包的場景
看網上還有一些場景:私有變數,安全,讓變數始終保持在記憶體中,可以避免全域性使用的變數都在window上等等,實際中的應用還不太清楚,網上資料也不多,還需磨練自己在實踐中體會。
在之前寫過的防抖和節流中,共同維護了一個定時器物件timer,其中產生了閉包,使得我們每次頻繁操作的時候,先判斷定時器狀態,再執行業務邏輯,達到防抖和節流的效果。
題目
第一道
下面兩段程式碼的執行結果是什麼?------來自阮一峰的網路日誌
var name = 'The Window';
var object = {
name: 'My Object',
getNameFunc: function () {
return function () {
console.log(this.name);
};
}
};
object.getNameFunc()(); // The window
var name = 'The Window';
var object = {
name: 'My Object',
getNameFunc: function () {
var that = this;
return function () {
console.log(that.name);
};
}
};
object.getNameFunc()(); // My Object
// 使用了父級函式的that變數,形成閉包
第二道
以下程式碼的輸出結果是什麼?
function fun(n, o) {
console.log(o);
return {
fun: function (m) {
return fun(m, n);
}
};
}
console.log('-----a-----');
var a = fun(0);
a.fun(1);
a.fun(2);
a.fun(3);
console.log('-----b-----');
var b = fun(0).fun(1).fun(2).fun(3);
console.log('-----c-----');
var c = fun(0).fun(1);
c.fun(2);
c.fun(3);
執行結果