標籤:運行 賦值 handler 理解 方法 str 建議 事件處理 書籍
前兩天去面試,被面試官問到平常寫關於什麼方面的閉包知識,頓時有點懵逼,雖然知道閉包是個大概什麼概念,但是在平常的工作中,貌似用的真的比較少,這幾天通過翻閱書籍,就想著來寫一篇關於閉包的文章,瞭解得比較淺,希望看到的大神可以多多指點。
1.什麼是閉包
閉包也就是指可以訪問其他函數範圍中變數的函數,通常建立閉包的方式就是在一個函數內建立一個函數。
2.閉包的特性
閉包主要有以下三個特性:
1.函數嵌套函數
2.函數內部可以訪問內部的參數和變數
3.被訪問的參數和變數不會被記憶體回收機制回收。
3.變數範圍
想要更好地理解閉包,就不得不理解js範圍和範圍鏈的概念。
js中的變數不外乎兩個:全域變數和局部變數;
js語言的特點就是函數內部可以訪問全域中定義的變數,但是函數外部卻不能訪問函數內部定義的局部變數
例1:var global = ‘global’; //定義一個全域變數
function test1(){
console.log(global);
};
test(); //輸出 global
例2:
註:js沒有塊級範圍的概念,只有函數範圍
4.如何從外部讀取內部變數?
由於js語言範圍的特點,一般情況下,我們是沒有辦法擷取局部變數的值。但是在實際的應用中,我們有時候需要訪問內部變數,這個時候閉包就派上用場了。
例:
這樣我們就可以訪問到函數內部定義的局部變數 ,函數test3返回的匿名函數就是一個閉包。
5.使用閉包的好處
那麼使用閉包到底有什麼好處呢,閉包的好處有以下幾點:
1.使一個變數長期駐紮在記憶體中
2.避免全域變數的汙染
3.私人成員的存在
一.變數長期駐紮在記憶體中
例:
解析:以上代碼的解析結果,可以看出,局部變數a在函數test4()執行完畢後,並沒有直接被銷毀,而是一直駐紮在記憶體中。
二.模組化代碼,減少全域變數的汙染
例:
解析:將匿名函數的傳回值直接賦值給test5變數,然後執行這個變數。
三.私人成員的存在
諸如Java在內的一些語言,支援把方法聲明為私人的,即只能被同一個類中的其他方法調用,就是不支援這樣的原生機制,但是可以通過閉包的方式進行類比私人方法。
例:
6.在迴圈中使用閉包:一個常見的錯誤
有時候會遇到在一個li列表中,點擊相應的li元素彈出其相應的index值。
例:
在瀏覽器中運行,會發現不論點擊哪個li,都只會彈出3,而不是其對應的index值,
該問題的蛀牙原因是賦值給li的onclick事件的是閉包函數而不是閉包對象,在 onclick 的回調被執行時,迴圈早已經完成。
運用另外一種方法可以解決這個問題,
7.效能考量
在不是非得使用閉包的情況下,建議不要使用閉包,因為閉包多指令碼效能有影響,同時會消耗大量的效能。
8.記憶體流失
如果閉包的範圍中儲存著對HTML元素,那就意味著改元素無法被銷毀。
例:function assignHandler(){
var element = document.getElementById(‘id‘);
element.onclick = function(){
console.log(element.id);
}
}
解析:以上代碼建立了一個作為element元素的事件處理常式的閉包,而在這個閉包內部又對改元素進行了引用。由於匿名函數儲存了對assignHandler()的函數對象的引用,所以對於element變數的引用至少為1,所以該變數會一直存在在記憶體中,不會被回收。可以通過改寫一下:
function assignHandler(){
var element = document.getElementById(‘id‘);
var id = element.id;
element.onclick = function(){
console.log(id);
};
element = null;
}
通過將element.id 的一個副本儲存在一個變數中,並在閉包中使用消除了循環參考,必須要記住:閉包會引用包含函數的整個使用中的物件,而這個使用中的物件中包含element。即使閉包不引用,但是包含函數的使用中的物件也會保持著對其的引用,所以有必要將其設定為null。
js閉包