在瞭解閉包之前,我們先來了解下局部變量和全局變量
局部變量
即爲定義在局部作用於下的變量,僅在這個作用域下被調用;
全局變量
即爲定義在window下,在任何地方都可以調用。
那麼他們有哪些優缺點呢?
局部變量用完會隨着當前程序一同被銷燬,不會佔用內存,但是不可以重複使用。
全局變量可以重複使用,但是很容易造成變量污染。
看來局部變量和全局變量都有他的優缺點,
那麼怎麼樣才能夠變量重用,又可以保護變量不會被污染的一種機制呢?
答案當然是有的,這就是今天要介紹的內容 閉包
什麼是閉包(closure)?
首先我們來看看分別在百度百科,菜鳥教程,W3Cschool和ECMAScript中對閉包的描述。
百度百科
閉包就是能夠讀取其他函數內部變量的函數。
菜鳥教程
閉包是一種保護私有變量的機制,在函數執行時形成私有的作用域,保護裏面的私有變量不受外界干擾。 直觀的說就是形成一個不銷燬的棧環境。
W3Cschool
JavaScript 變量可以是局部變量或全局變量。
私有變量可以用到閉包。
ECMAScript
閉包,指的是詞法表示包括不被計算的變量的函數,也就是說,函數可以使用函數之外定義的變量。
綜上所述,閉包就是外層函數的內層函數可以使用外層函數的變量。
怎麼去理解這句話呢?
我們都知道,一般函數在執行完後會出棧,但是閉包導致作用域鏈不被釋放,所有內層函數即使在外層函數執行完成後仍然可以訪問外層函數裏的變量。
function test(){
var a = 100;
return function (){
console.log(a);
}
}
var result = test();
result();
如上代碼所示,代碼在預編譯
GO
存在test
和result
,test
指向function test(){...}
函數體,result
爲undefined
,當函數test()
執行時,test
的AO
存在a
,執行後a
的值100
,在GO
中的result
賦值爲function(){console.log(a)}
,此時test
出棧,但是因爲test
內變量a
被result
所引用,所以test
的AO
無法被釋放,result
可以訪問到test
的AO
內的值,其中a
爲受保護的變量。
閉包在開發中有什麼作用?
在開發中我們會經常的使用動態添加點擊事件,我們按照思維通常會寫成下面這樣
function test(){
var liList = document.getElementsByTagName("li");
for(var i = 0; i < liList.length;i++){
liList[i].onclick = function(){
console.log(i);
}
}
}
test()
但是因爲我們在外層函數裏使用了內層函數,並且使用了外層函數的變量i
,形成了閉包;
內層函數每一次循環都可以調用外層函數的值並且每次自增1
,而點擊事件返回的函數體,此時並沒有去執行;
當我們觸發點擊事件時,此時外層函數的i
爲liList.length
,則每一次點擊的i
爲liList.length
,與我們預想的結果不同。
怎麼去解決閉包所帶來的問題?
閉包無非就是返回的函數內受保護的變量沒有被放入返回的函數體內,只是返回了函數的內容,那麼我們只需要讓他返回具體的值。
function test(){
var liList = document.getElementsByTagName("li");
for(var i = 0; i < liList.length;i++){
(function(j){
liList[j].onclick = function(){
console.log(j);
}
}(i))
}
}
test()
如上代碼所示,i
是變化的,但是隻能返回i
,不能返回i
的值;我們定義一個匿名的函數,講i
的值傳入這個匿名函數,則每次返回的爲console.log(1);console.log(2)......