(1)知識(shí)點(diǎn)
- (1.1)定義
- (1.2)變量的作用域
- (1.3)如何從外部讀取局部變量
- (1.4)判斷閉包
- (1.5)用途
- (1.6)使用注意點(diǎn)
- (1.7)面試題
(2)細(xì)化
(2.1)定義
能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)稱(chēng)之為閉包舵盈。
(2.2)變量的作用域
要理解閉包当凡,首先要先理解閉包的作用域:全局變量和局部變量
全局變量:即定義在函數(shù)外部的變量
優(yōu):反復(fù)使用,且共享使用
缺:可能隨時(shí)在任意位置被篡改——全局污染
局部變量:使用var聲明楞遏,定義在函數(shù)內(nèi)部的變量
不可反復(fù)使用曲伊!方法調(diào)用完自動(dòng)釋放
函數(shù)內(nèi)部聲明變量的時(shí)候髓梅,一定要使用var命令。如果不用的話壳繁,你實(shí)際上聲明了一個(gè)全局變量震捣!
Tips:盡量避免使用全局變量
(2.3)如何從外部讀取局部變量
使用閉包荔棉,即在函數(shù)內(nèi)部再定義一個(gè)函數(shù)
function f1(){
var n=999;
function f2(){
alert(n); // 999
}
}
函數(shù)f2就被包括在函數(shù)f1內(nèi)部,這時(shí)f1內(nèi)部的所有局部變量蒿赢,對(duì)f2都是可見(jiàn)的润樱;但是反過(guò)來(lái)就不行,f2內(nèi)部的局部變量羡棵,對(duì)f1就是不可見(jiàn)的壹若。這就是Javascript語(yǔ)言特有的"鏈?zhǔn)阶饔糜?結(jié)構(gòu)(chain scope),子對(duì)象會(huì)一級(jí)一級(jí)地向上尋找所有父對(duì)象的變量皂冰。
這個(gè)時(shí)候只需要將f2作為返回值店展,那么在外部就可以讀取到f1中的變量n了
function f1(){
var n=999;
function f2(){
alert(n);
}
return f2;
}
var result=f1();
result(); // 999
由此,f2函數(shù)即形成了閉包秃流。
(2.4)判斷閉包
a. 內(nèi)外層函數(shù)嵌套
b. 內(nèi)層函數(shù)必須使用了外層函數(shù)的局部變量
c. 外層函數(shù)將內(nèi)層函數(shù)返回到外部赂蕴,可在外部調(diào)用
Tips:外層函數(shù)調(diào)用了幾次,就有幾個(gè)受保護(hù)的局部變量副本
(2.5)閉包的作用
a. 可以讀取函數(shù)內(nèi)部的變量
b. 使得這些變量的值始終保持在內(nèi)存中剔应,不會(huì)在調(diào)用結(jié)束后睡腿,被垃圾回收機(jī)制回收
function f1(){
var n=999;
nAdd=function(){n+=1} //匿名函數(shù)
function f2(){
alert(n);
}
return f2;
}
var result=f1();
result(); // 999
nAdd();
result(); // 1000
result實(shí)際上就是閉包f2函數(shù)。它一共運(yùn)行了兩次峻贮,第一次的值是999席怪,第二次的值是1000。這證明了纤控,函數(shù)f1中的局部變量n一直保存在內(nèi)存中挂捻,并沒(méi)有在f1調(diào)用后被自動(dòng)清除。
(2.6)使用注意點(diǎn)
a. 由于閉包會(huì)使得函數(shù)中的變量都被保存在內(nèi)存中船万,內(nèi)存消耗很大刻撒,所以不能濫用閉包,否則會(huì)造成網(wǎng)頁(yè)的性能問(wèn)題耿导,在IE中可能導(dǎo)致內(nèi)存泄露声怔。解決方法是,在退出函數(shù)之前舱呻,將不使用的局部變量全部刪除醋火。
b. 閉包會(huì)在父函數(shù)外部,改變父函數(shù)內(nèi)部變量的值箱吕。所以芥驳,如果你把父函數(shù)當(dāng)作對(duì)象(object)使用,把閉包當(dāng)作它的公用方法(Public Method)茬高,把內(nèi)部變量當(dāng)作它的私有屬性(private value)兆旬,這時(shí)一定要小心,不要隨便改變父函數(shù)內(nèi)部變量的值怎栽。
(2.7)面試題
function outer(){
for(var i=0,arr=[];i<3;i++){//i受保護(hù)的變量
arr[i]=function(){return i};
}//i變成了3
return arr;
}
var funs=outer(); //外層函數(shù)調(diào)用一次丽猬,只有1個(gè)i
console.log(funs[0]()); //3
console.log(funs[1]()); //3
console.log(funs[2]()); //3
(3)實(shí)踐
<script type="text/javascript">
/*定義一個(gè)函數(shù)宿饱,模擬取號(hào)機(jī):
每次取一個(gè)數(shù)
數(shù)是連續(xù)不重復(fù)的
*/
//定義工廠函數(shù)保護(hù)局部變量
function factory(){
var n=0;//受保護(hù)的局部變量
return function(){ return ++n; }
}
//獲得返回的內(nèi)部函數(shù)對(duì)象
var ccb=factory();//外層函數(shù)調(diào)用第一次
//ccb:function(){ return ++n; }
//使用內(nèi)部函數(shù)對(duì)象操作受保護(hù)的變量
console.log(ccb());//1
console.log(ccb());//2
n=1;
console.log(ccb());//3
console.log("----------");
var icbc=factory();//外層函數(shù)調(diào)用第二次
console.log(icbc());//1
console.log(icbc());//2
console.log("----------");
</script>
參考學(xué)習(xí):
學(xué)習(xí)Javascript閉包 ---- 阮一峰