閉包的形成跟變量的作用域以及變量的生存周期密切相關
一、變量的作用域,是指變量的有效范圍
當在函數(shù)中聲明一個變量的時候,如果該變量前面沒有帶上關鍵字?var,這個變量就會成為全局變量 ,這當然是一種很容易造成命名沖突的做法。
另外一種情況是用?var?關鍵字在函數(shù)中聲明變量,這時候的變量即是局部變量,只有在該函數(shù)內(nèi)部才能訪問到這個變量,在函數(shù)外面是訪問不到的。
例:變量的搜索是從內(nèi)到外而非從外到 內(nèi)的
vara=1;
varfunc1 =function(){
varb=2;
varfunc2 =function(){
varc=3;
console.log ( b );// 輸出:2
console.log ( a );// 輸出:1
? ? }
? ? func2();
console.log(c);//輸出:Uncaught ReferenceError: c is not defined
};
func1();
對于全局變量來說,全局變量的生存周期當然是的永久,除非我們主動銷毀這個全局變量。
而對于在函數(shù)內(nèi)用var關鍵字聲明的局部變量來說,當退出函數(shù)時,這些局部變量即失去了 它們的價值,它們都會隨著函數(shù)的調(diào)用的結束而銷毀
例一:
varfunc =function(){
vara=1;
returnfunction(){
? ? ? ? a++;
console.log(a);
? ? }
};
varf=func();
f();// 輸出:2
f();// 輸出:3
f();// 輸出:4
f();// 輸出:5
跟我們之前的結論相反,上面的例子在當退出函數(shù)后,局部變量a?并沒有消失,而是似乎一直在某個地方 存活著。這是因為當執(zhí)行?var f = func();時,f?返回了一個名函數(shù)的引用,它可以問到func()?被調(diào)用時產(chǎn)生的環(huán)境,而局部變量?a?一直處在這個環(huán)境里。既然外局部變量所在的環(huán)境還能被外 界訪問,這個局部變量就有了不被銷毀的理由。在這里生了一個閉包結構,局部變量的聲明看起來被延續(xù)了。
例二,假設頁面上有 5 個 div 節(jié)點,我們通過循環(huán)來給每個 div綁定 onclick 事件,按照索引順序,點擊第 1 個 div 時彈出 0,點擊第 2 個 div 時出 1,以此類
varnodes =document.getElementsByTagName('div');
for(vari=0,len=nodes.length;i<len;i++){
nodes[ i ].onclick =function(){
? ? ? ? alert(i);
? ? }
};
測試這段代碼會發(fā)現(xiàn),無論點擊哪個 div,最后彈出的結果都是 5
這是因為?div?節(jié)點的?onclick?事件是被異步觸發(fā)的,當事件被觸發(fā)的時候,for循環(huán)早已結束,此時 i 的值已經(jīng)是?5,
所以在?div的?onclick?事件函數(shù)中順著作用域鏈從內(nèi)到外查找變量 i 時,查找到的值總是?5。
解決方法是在閉包的幫助下,每次循環(huán)的i?值都封閉起來。當在事件函數(shù)中順著作用域鏈從內(nèi)到外查找變量?i時,會先找到被封閉在閉包環(huán)境中的i,如果有5個div,這里的i分別 是?0,1,2,3,4
for(vari=0,len=nodes.length;i<len;i++){
(function( i ){
nodes[ i ].onclick =function(){
console.log(i);
? ? ? ? }
? ? })(i)
};
1、封裝變量—-閉包可以幫助一些不需要暴露在全局的變量封裝成“私有變量”
例一,計算乘積
varmult =function(){
vara=1;
for(vari=0,l=arguments.length;i<l;i++){
a = a *arguments[i];
? ? }
returna;
};
functiona(){
vari =0;
functionb(){
console.log(++i);
? ? }
returnb;
}
varc = a();
c();
首先有一個封閉的函數(shù)a(即自定義的一個function a()方法),該函數(shù)內(nèi)部的變量b(局部變量/局部方法)外部無法直接調(diào)用;但如果把這個函數(shù)賦值給一個全部變量c時,
全局變量c就獲取到了函數(shù)局部變量b的值,從而使局部變量b的值得到了保存,即延長了一個局部變量b的生命周期,除非主動銷毀這個全局變量c。
此時,我們也就制造出來了一個“閉包”。簡單說“閉包是指有權限訪問另一個函數(shù)作用域的變量的函數(shù)”。
在這個過程中,因為局部變量b的聲明周期延長,使得Javascript的垃圾回收機制不會收回函數(shù)a所占用的資源,因為函數(shù)a的局部變量b的執(zhí)行需要依賴函數(shù)a中的變量。