淺談JavaScript的閉包函數(shù)
在JavaScript中,閉包恐怕是很多人不能理解的一個概念了,甚至很多人也會把閉包和匿名函數(shù)混淆。
閉包是有權(quán)訪問另一個函數(shù)作用域中的變量的函數(shù)。首先要明白的就是,閉包是函數(shù)。由于要求它可以訪問另一個函數(shù)的作用于中的變量,所以我們往往是在一個函數(shù)的內(nèi)部創(chuàng)建另一個函數(shù),而“另一個函數(shù)”就是閉包。
比如之前提到過的作為比較函數(shù):
function createComparisonFunction(propertyName){
return function(object1,object2){
var value1=object1[propertyName];
var value2=object2[propertyName];
if(value1<value2){ return="" -1;="" }else="" if(value1="">value2){
return 1;
}else{
return 0;
}
};
}
</value2){>
在這個函數(shù)中,由于return的函數(shù)它訪問了包含函數(shù)(外部函數(shù))的變量propertyName,所以我們認(rèn)為這個函數(shù)即為閉包。即使這個閉包被返回了,而且是在其他地方調(diào)用了,但是它仍然可以訪問propertyName,之所以還能夠訪問到propertyName這個變量,是因為內(nèi)部函數(shù)(閉包)的作用域鏈中包含著createComparisonFunction函數(shù)的作用域。因此,要徹底搞清楚閉包,就需要徹底搞清楚函數(shù)被調(diào)用時發(fā)生了什么以及作用域鏈的有關(guān)知識。
當(dāng)某個函數(shù)被調(diào)用時,會創(chuàng)建一個執(zhí)行環(huán)境(函數(shù)一旦被調(diào)用,則進(jìn)入函數(shù)執(zhí)行環(huán)境)和相應(yīng)的作用域鏈(作用域鏈?zhǔn)请S著執(zhí)行環(huán)境的不同而動態(tài)變化的)。(對于函數(shù)而言)之后使用arguments和其他命名參數(shù)的值來初始化函數(shù)的活動對象(每個執(zhí)行環(huán)境都有一個變量對象,對于函數(shù)成為活動對象)。對于有閉包的函數(shù)而言,在作用域鏈中,外部函數(shù)的活動對象始終處于第二位,外部函數(shù)的外部函數(shù)的活動對象始終處于第三位。。。直至作為作用域鏈終點的全局執(zhí)行環(huán)境。
下面撇開閉包不談,先通過一個簡單的例子來理解作用域鏈以及變量對象和活動對象。
function compare(value1,value2){
if(value1<value2){
return -1;
}else if(value1>value2){
return 1;
}else{
return 0;
}
}<br> var result=compare(5,10);
以上代碼首先定義了compare()函數(shù),然后又在全局作用域中調(diào)用了它。當(dāng)調(diào)用compare函數(shù)時,首先創(chuàng)建一個函數(shù)執(zhí)行環(huán)境,每個執(zhí)行環(huán)境又對應(yīng)這一個變量對象,也就是說作用域鏈和函數(shù)執(zhí)行環(huán)境是同時創(chuàng)建的,其中作用域鏈的前端即為compare函數(shù)的活動對象(在函數(shù)中,變量對象又稱為活動對象)。在compare活動對象中包含了arguments、value1、value2(關(guān)鍵:盡管arguments數(shù)組對象包含value1和value2,但是我們還是要分開列舉,而不是僅僅認(rèn)為只有arguments包含于compare的活動對象,因為value1和value2也包含于compare的活動對象)。
對于上述代碼而言,全局執(zhí)行環(huán)境的變量對象(再次聲明:每一個執(zhí)行環(huán)境都存在相應(yīng)的變量對象)中包含result和compare,該變量對象在compare()執(zhí)行環(huán)境的作用域鏈的第二位。
當(dāng)我們創(chuàng)建compare()函數(shù)時,會創(chuàng)建一個預(yù)先包含全局變量對象的作用域鏈,這個作用域鏈被保存在compare函數(shù)內(nèi)部的[[scope]]屬性中,當(dāng)調(diào)用compare函數(shù)時,會為函數(shù)創(chuàng)建一個執(zhí)行環(huán)境,然后通過復(fù)制函數(shù)的[[scope]]屬性中的對象構(gòu)建起執(zhí)行環(huán)境的作用域鏈。如下:

作用域鏈的本質(zhì)就是一個指向變量對象的指針列表,它只引用但不實際包含變量對象。無論什么時候在函數(shù)中訪問一個變量,就會從作用域鏈的前端沿著作用域鏈搜索具有相應(yīng)名字的變量。我們知道,全局環(huán)境的變量對象始終存在,而局部環(huán)境(如compare()函數(shù)執(zhí)行環(huán)境)的變量對象只在函數(shù)執(zhí)行的時候存在,一旦執(zhí)行完畢,局部變量對象(活動對象)就會被銷毀。但在閉包中,卻與此不同。
把博文開始的代碼復(fù)制如下:
function createComparisonFunction(propertyName){
return function(object1,object2){
var value1=object1[propertyName];
var value2=object2[propertyName];
if(value1<value2){
return -1;
}else if(value1>value2){
return 1;
}else{
return 0;
}
};
}
由于在一個函數(shù)內(nèi)部定義的函數(shù)會將包含函數(shù)(即外部函數(shù))的活動對象添加到它的作用域鏈中。因此,在createComparisonFunction函數(shù)內(nèi)部定義的匿名函數(shù)的作用域中實際包含著外部函數(shù)的活動對象。如果我們執(zhí)行如下代碼:
var compare=createComparisonFunction("name");
var result=compare({name:"zzw"},{name:"ht"});
這時候匿名函數(shù)的作用域鏈將引用著外部函數(shù)的活動對象。因為匿名函數(shù)從外部函數(shù)中被返回后,它的作用域鏈被初始化為包含外部函數(shù)的活動對象和全局變量對象。這樣,匿名函數(shù)就可以訪問外部函數(shù)中定義的所有變量。更為重要的是,即使外部函數(shù)在執(zhí)行完畢后,其活動對象也不會被銷毀,因為匿名函數(shù)的作用域鏈仍然在引用這個活動對象。換句話說,當(dāng)createComparison()函數(shù)返回后,其執(zhí)行環(huán)境的作用域鏈會被銷毀,但是她的活動對象仍然保存在內(nèi)存中。等到倪敏函數(shù)被銷毀后,外部函數(shù)的活動對象才會被銷毀。
由于閉包會攜帶者包含他的函數(shù)的作用域,因此回避其他函數(shù)占用更多的內(nèi)存。過度的使用閉包可能會導(dǎo)致內(nèi)存占用過多,我們建議只在絕對必要的時候再考慮使用閉包。
模仿塊級作用域
(function(){
var now=new Date();
if(now.getMonth()==0&&now.getDate()==1){
alert("happy new year");
}
})();
這就是模仿塊級作用域,即定義并立即調(diào)用了一個匿名函數(shù)。
如下為演示其作用:
function outputNumbers(count){
(function(){
for (var i=0;i<count;i++){
console.log(i);
}
})();
console.log(i);
}
outputNumbers(5);
這是在模仿塊級作用域之外的console.log(i)就會導(dǎo)致錯誤,因為i未被定義。說明在執(zhí)行了模仿塊級作用域之后,內(nèi)部的變量就被銷毀了。
以上就是本文的全部內(nèi)容,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作能帶來一定的幫助,如果有疑問大家可以留言交流,同時也希望多多支持腳本之家!
- JavaScript閉包函數(shù)訪問外部變量的方法
- javascript中的return和閉包函數(shù)淺析
- js實現(xiàn)拖拽 閉包函數(shù)詳細(xì)介紹
- javascript高級編程之函數(shù)表達(dá)式 遞歸和閉包函數(shù)
- javascript 閉包函數(shù)做顯隱內(nèi)容
- 輕松學(xué)習(xí)Javascript閉包函數(shù)
- JavaScript 匿名函數(shù)(anonymous function)與閉包(closure)
- js bind 函數(shù) 使用閉包保存執(zhí)行上下文
- 弱類型語言javascript中 a,b 的運算實例小結(jié)
相關(guān)文章
JS+DIV+CSS實現(xiàn)的經(jīng)典標(biāo)簽切換效果代碼
這篇文章主要介紹了JS+DIV+CSS實現(xiàn)的經(jīng)典標(biāo)簽切換效果代碼,涉及JavaScript基于鼠標(biāo)事件針對頁面元素動態(tài)變換的實現(xiàn)技巧,頁面美觀實用,需要的朋友可以參考下2015-09-09
javascript中的Function.prototye.bind
這篇文章主要介紹了javascript中的Function.prototye.bind的相關(guān)資料,需要的朋友可以參考下2015-06-06
javascript實現(xiàn)點擊按鈕彈出一個可關(guān)閉層窗口同時網(wǎng)頁背景變灰的方法
這篇文章主要介紹了javascript實現(xiàn)點擊按鈕彈出一個可關(guān)閉層窗口同時網(wǎng)頁背景變灰的方法,涉及javascript鼠標(biāo)事件及頁面元素樣式操作的相關(guān)技巧,需要的朋友可以參考下2015-05-05
實現(xiàn)連綴調(diào)用的map方法(prototype)
實現(xiàn)連綴調(diào)用的map方法(prototype),需要學(xué)習(xí)的朋友可以參考下。2009-08-08

