長久以來三幻,閉包是前端同學(xué)面試必考的問題。會用閉包也成了高級前端開發(fā)者的標志呐能,今天就來徹底弄清楚閉包的每一個細節(jié)念搬。
1.閉包是什么?
比較官方的定義是:一個擁有許多變量和綁定了這些變量的環(huán)境的表達式(通常是一個函數(shù))摆出,因而這些變量也是該表達式的一部分朗徊。
我個人的簡化理解是:閉包就是函數(shù)及函數(shù)上下文環(huán)境的集合。上下文可以理解為函數(shù)可以訪問到的所有變量偎漫。
很多人肯定這樣寫過JavaScript代碼:
var paramA = "test";
var functionA = function(){
console.log(paramA);
}
這樣寫其實就是使用了閉包的概念爷恳,只是很多人并沒有意識到這是閉包。這段代碼值得注意的地方是函數(shù)functionA內(nèi)部調(diào)用了函數(shù)外的變量paramA象踊。這就是閉包的鮮明特征温亲。
2.為什么會有閉包棚壁?
閉包是JavaScript鏈式作用域的副產(chǎn)品。閉包不是JavaScript獨有的特性铸豁,和JavaScript有類似作用域設(shè)計的語言也存在閉包灌曙,比如Python。
鏈式作用域的設(shè)計決定了子作用域中的函數(shù)可以訪問到父級作用域的變量节芥,嵌套函數(shù)可以訪問到外部函數(shù)的變量在刺。
借用一張圖來說明一下JavaScript的鏈式作用域。
3.閉包如何使用头镊?
前面的小例子雖然體現(xiàn)了閉包的特征蚣驼,但并不是一個真正的閉包函數(shù)∠嗤В看下面這個例子:
function a() {
var i = 0;
function b() { console.log(i++); }
return b;
}
var c = a();
c();
這里函數(shù)b就是一個閉包函數(shù)颖杏,那為什么要用c去調(diào)用函數(shù)a呢?這是為了防止JavaScript的垃圾回收機制生效坛芽。在Javascript中留储,如果一個對象不再被引用,那么這個對象就會被GC回收咙轩。如果兩個對象互相引用获讳,而不再被第3者所引用,那么這兩個互相引用的對象也會被回收活喊。因為函數(shù)a被b引用丐膝,a又被c引用。這樣函數(shù)a和b才不會被回收钾菊。
4.閉包有什么作用帅矗?
我認為閉包的主要作用有兩點:
1.保持變量常駐內(nèi)存,不被回收煞烫。
2.實現(xiàn)私有的方法和屬性浑此,禁止外部訪問。
第一點很好理解滞详,比如上面的例子中i就會常駐內(nèi)存尤勋,每次執(zhí)行c函數(shù)都會得到i的值。這種情況很常見茵宪,比如游戲中的的的分數(shù)等最冰。
第二點是閉包最重要的用處,還是以上面的例子來說稀火。i是函數(shù)a的局部變量暖哨,如果想修改i的值,只有調(diào)用函數(shù)b。其他外部函數(shù)是無法訪問到變量i的篇裁,這樣就保證了變量i的安全沛慢。
5.閉包的缺點
事物都有兩面性,閉包在帶來方便的同時也有一些弊端达布。
因為閉包函數(shù)會使變量常駐內(nèi)存团甲,如果使用不當。比如在循環(huán)中使用閉包黍聂,有可能導(dǎo)致內(nèi)存壓力過大躺苦。
在IE中會導(dǎo)致內(nèi)存泄漏,這是IE的bug并不是閉包的問題产还。
6.我的觀點
我猜測閉包是JavaScript在設(shè)計之初未曾想到過的用法匹厘,如果業(yè)務(wù)中沒有強烈的需求,盡量不要使用閉包脐区。