javascript
深入理解JavaScript的闭包特性如何给循环中的对象添加事件
| 初學者經(jīng)常碰到的,即獲取HTML元素集合,循環(huán)給元素添加事件。在事件響應函數(shù)中(event handler)獲取對應的索引。但每次獲取的都是最后一次循環(huán)的索引。原因是初學者并未理解JavaScript的閉包特性。 有個網(wǎng)友問了個問題,如下的html,為什么點擊所有的段落p輸出都是5,而不是alert出對應的0,1,2,3,4。 1.? <!DOCTYPE?HTML> 2.? <html> 3.? <head> 4.? <meta?charset="utf-8"?/> 5.? <title>閉包演示</title> 6.? <style?type="text/css"> 7.? ????p?{background:gold;} 8.? </style> 9.? <script?type="text/javascript"> 10.function?init()?{ 11.????var?pAry?=?document.getElementsByTagName("p"); 12.????for(?var?i=0;?i<pAry.length;?i++?)?{ 13.?????????pAry[i].onclick?=?function()?{ 14.?????????alert(i); 15.????} 16.??} 17.} 18.</script> 19.</head> 20.<body?οnlοad="init();"> 21.<p>產(chǎn)品?0</p> 22.<p>產(chǎn)品?1</p> 23.<p>產(chǎn)品?2</p> 24.<p>產(chǎn)品?3</p> 25.<p>產(chǎn)品?4</p> 26.</body> 27.</html> 以上場景是初學者經(jīng)常碰到的。即獲取HTML元素集合,循環(huán)給元素添加事件。在事件響應函數(shù)中(event handler)獲取對應的索引。但每次獲取的都是最后一次循環(huán)的索引。 原因是初學者并未理解JavaScript的閉包特性。通過element.οnclick=function(){alert(i);}方式給元 素添加點擊事件。響應函數(shù)function(){alert(i);}中的 i 并非每次循環(huán)時對應的 i(如0,1,2,3,4)而是循環(huán)后最后 i 的值5。 或者說循環(huán)時響應函數(shù)內(nèi)并未能保存對應的值 i,而是最后一次i++的值5。 了解了原因,下面就由幾種方式可與解決: 1、將變量 i 保存給在每個段落對象(p)上 1.? function?init1()?{ 2.? ??var?pAry?=?document.getElementsByTagName("p"); 3.? ??for(?var?i=0;?i<pAry.length;?i++?)?{ 4.? ?????pAry[i].i?=?i; 5.? ?????pAry[i].onclick?=?function()?{ 6.? ????????alert(this.i); 7.? ?????} 8.? ??} 9.? } 2、將變量 i 保存在匿名函數(shù)自身 1.? function?init2()?{ 2.? ??var?pAry?=?document.getElementsByTagName("p"); 3.? ??for(?var?i=0;?i<pAry.length;?i++?)?{ 4.? ???(pAry[i].onclick?=?function()?{ 5.? ????????alert(arguments.callee.i); 6.? ????}).i?=?i; 7.? ??} 8.? } 3、加一層閉包,i 以函數(shù)參數(shù)形式傳遞給內(nèi)層函數(shù) 1.? function?init3()?{ 2.? ??var?pAry?=?document.getElementsByTagName("p"); 3.? ??for(?var?i=0;?i<pAry.length;?i++?)?{ 4.? ???(function(arg){ 5.? ???????pAry[i].onclick?=?function()?{ 6.? ??????????alert(arg); 7.? ???????}; 8.? ???})(i);//調(diào)用時參數(shù) 9.? ??} 10.} 4、加一層閉包,i 以局部變量形式傳遞給內(nèi)層函數(shù) 1.? function?init4()?{ 2.? ??var?pAry?=?document.getElementsByTagName("p"); 3.? ??for(?var?i=0;?i<pAry.length;?i++?)?{ 4.? ????(function?()?{ 5.? ??????var?temp?=?i;//調(diào)用時局部變量 6.? ??????pAry[i].onclick?=?function()?{ 7.? ????????alert(temp); 8.? ??????} 9.? ????})(); 10.??} 11.} 5、加一層閉包,返回一個函數(shù)作為響應事件(注意與3的細微區(qū)別) 1.? function?init5()?{ 2.? ??var?pAry?=?document.getElementsByTagName("p"); 3.? ??for(?var?i=0;?i<pAry.length;?i++?)?{ 4.? ???pAry[i].onclick?=?function(arg)?{ 5.? ???????return?function()?{//返回一個函數(shù) 6.? ???????alert(arg); 7.? ?????} 8.? ???}(i); 9.? ??} 10.} 6、用Function實現(xiàn),實際上每產(chǎn)生一個函數(shù)實例就會產(chǎn)生一個閉包 1.? function?init6()?{ 2.? ????var?pAry?=?document.getElementsByTagName("p"); 3.? ????for(?var?i=0;?i<pAry.length;?i++?)?{ 4.? ??????pAry[i].onclick?=?new?Function("alert("?+?i?+?");");//new一次就產(chǎn)生一個函數(shù)實例 5.? ????} 6.? } 7、用Function實現(xiàn),注意與6的區(qū)別 1.? function?init7()?{ 2.? ????var?pAry?=?document.getElementsByTagName("p"); 3.? ????for(?var?i=0;?i<pAry.length;?i++?)?{ 4.? ?????????pAry[i].onclick?=?Function('alert('+i+')'); 5.? ????} 6.? } |
?
?
淺析Javascript閉包的特性
?
本文將對Javascript閉包的特性進行分析,并舉例進行說明。閉包,是指語法域位于某個特定的區(qū)域,具有持續(xù)參照(讀寫)位于該區(qū)域內(nèi)自身范圍之外的執(zhí)行域上的非持久型變量值能力的段落。
AD:
Javascript閉包的定義非常晦澀——閉包,是指語法域位于某個特定的區(qū)域,具有持續(xù)參照(讀寫)位于該區(qū)域內(nèi)自身范圍之外的執(zhí)行域上的非持久型變量值能力的段落。這些外部執(zhí)行域的非持久型變量神奇地保留它們在閉包最初定義(或創(chuàng)建)時的值(深連結(jié))。
簡單來說,Javascript閉包就是在另一個作用域中保存了一份它從上一級函數(shù)或作用域取得的變量(鍵值對),而這些鍵值對是不會隨上一級函數(shù)的執(zhí)行完成而銷毀。周愛民說得更清楚,閉包就是“屬性表”,閉包就是一個數(shù)據(jù)塊,閉包就是一個存放著“Name=Value”的對照表。就這么簡單。但是,必須強調(diào),閉包是運行期概念,一個函數(shù)實例。
Javascript閉包的實現(xiàn),通常是在函數(shù)內(nèi)部再定義函數(shù),讓該內(nèi)部函數(shù)使用上一級函數(shù)的變量或全局變量。
ECMAScript認為使用全局變量是一個簡單的Javascript閉包實例。
1.??var?sMessage?=?"Hello?World";? ?
2.??function?sayHelloWorld(){? ?
3.? alert(sMessage);? ?
4.? };? ?
5.? sayHelloWorld();?
但它完成沒有體現(xiàn)Javascript閉包的特性……
現(xiàn)在比較讓人認同的Javascript閉包實現(xiàn)有如下三種
1.??with(obj){? ?
2.? //這里是對象閉包? ?
3.? }(function(){???? ?
4.? //函數(shù)閉包? ?
5.? })()try{? ?
6.? //...? ?
7.? }?catch(e)?{? ?
8.? //catch閉包?但IE里不行? ?
9.? }?
附上今天在無憂看到的問題:
要求:
讓這三個節(jié)點的Onclick事件都能正確的彈出相應的參數(shù)。
1.??<ul>?? ?
2.??<li?id="a1">aa</li>?? ?
3.??<li?id="a2">aa</li>? ?
4.??<li?id="a3">aa</li>? ?
5.??</ul>? ?
6.??<script?type="text/javascript">? ?
7.??<ul>? ?
8.??<li?id="a1">aa</li>? ?
9.??<li?id="a2">aa</li>? ?
10.?<li?id="a3">aa</li>? ?
11.?</ul>? ?
12.?<script?type="text/javascript">? ?
13. for(var?i=1;?i?<?4;?i++){? ?
14. var?id?=?document.getElementById("a"?+?i);? ?
15. id.onclick?=?function(){? ?
16. alert(i);//現(xiàn)在都是返回4???? ?
17. }? ?
18. }? ?
19.?</script>?
客服果果的解答:
1.? for(var?i=1;?i?<?4;?i++){???? ?
2.? var?id?=?document.getElementById("a"?+?i);??? ?
3.? /*??? ?
4.? 這里生成了一個匿名函數(shù)并賦值給對象?id_i;??? ?
5.? */??? ?
6.? id.onclick?=?function(){???????? ?
7.? /*???????? ?
8.? 這個i來源于局部變量,無法以window.i或者obj.i的形式在后期引用,???????? ?
9.? 只好以指針或者變量地址方式保存在這個匿名函數(shù)中,???????? ?
10. 這就是傳說的閉包,所以所有這個過程中生成的事件句柄都使用引用???????? ?
11. 的方式來持久這個變量,也就是這些匿名函數(shù)共用一個變量i;???????? ?
12. */??????? ?
13. alert(i);???? ?
14. };? ?
15. };?
局部變?nèi)?/p>
1.??for(var?i=1;?i?<?4;?i++){? ?
2.? var?id?=?document.getElementById("a"?+?i);??? ?
3.? id.i=i;//這個i有了根??? ?
4.? id.οnclick=function(){???????? ?
5.? alert(this.i)???? ?
6.? };? ?
7.? };1.for(var?i=1;?i?<?4;?i++){??? ?
8.? var?id?=?document.getElementById("a"?+?i);?? ?
9.? window[id.id]=i;//這個i有了根?? ?
10. id.οnclick=function(){??????? ?
11. alert(window[this.id]);??? ?
12. };? ?
13. }?
產(chǎn)生一對一的更多Javascript閉包
1.??for(var?i=1;?i?<?4;?i++){?? ?
2.? var?id?=?document.getElementById("a"?+?i);?? ?
3.? id.onclick?=?new?function(){?????? ?
4.? var?i2=i;//這個i是閉包的閉包????? ?
5.??return?function(){????????? ?
6.? alert(i2);????? ?
7.? }??? ?
8.? };? ?
9.? }?
javascript深入理解js閉包發(fā)布:dxy 字體:[增加 減小] 類型:轉(zhuǎn)載
?
閉包(closure)是Javascript語言的一個難點,也是它的特色,很多高級應用都要依靠閉包實現(xiàn)。
一、變量的作用域
?
要理解閉包,首先必須理解Javascript特殊的變量作用域。
?
變量的作用域無非就是兩種:全局變量和局部變量。
?
Javascript語言的特殊之處,就在于函數(shù)內(nèi)部可以直接讀取全局變量。
?
?
Js代碼
?
var n=999;
?
function f1(){
alert(n);
}
?
f1(); // 999
?
另一方面,在函數(shù)外部自然無法讀取函數(shù)內(nèi)的局部變量。
?
Js代碼
?
function f1(){
var n=999;
}
?
alert(n); // error
?
這里有一個地方需要注意,函數(shù)內(nèi)部聲明變量的時候,一定要使用var命令。如果不用的話,你實際上聲明了一個全局變量!
?
Js代碼
?
function f1(){
n=999;
}
?
f1();
?
alert(n); // 999
?
--------------------------------------------------------------------------------------------------------
?
二、如何從外部讀取局部變量?
?
出于種種原因,我們有時候需要得到函數(shù)內(nèi)的局部變量。但是,前面已經(jīng)說過了,正常情況下,這是辦不到的,只有通過變通方法才能實現(xiàn)。
?
那就是在函數(shù)的內(nèi)部,再定義一個函數(shù)。
?
Js代碼
?
function f1(){
?
n=999;
?
function f2(){
alert(n); // 999
}
?
}
?
在上面的代碼中,函數(shù)f2就被包括在函數(shù)f1內(nèi)部,這時f1內(nèi)部的所有局部變量,對f2都是可見的。但是反過來就不行,f2內(nèi)部的局部變量,對f1 就是不可見的。這就是Javascript語言特有的“鏈式作用域”結(jié)構(gòu)(chain scope),
?
子對象會一級一級地向上尋找所有父對象的變量。所以,父對象的所有變量,對子對象都是可見的,反之則不成立。
?
既然f2可以讀取f1中的局部變量,那么只要把f2作為返回值,我們不就可以在f1外部讀取它的內(nèi)部變量了嗎!
?
?
Js代碼
?
function f1(){
?
n=999;
?
function f2(){
alert(n);
}
?
return f2;
?
}
?
var result=f1();
?
result(); // 999
?
--------------------------------------------------------------------------------------------------------
?
三、閉包的概念
?
上一節(jié)代碼中的f2函數(shù),就是閉包。
?
各種專業(yè)文獻上的“閉包”(closure)定義非常抽象,很難看懂。我的理解是,閉包就是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)。
?
由于在Javascript語言中,只有函數(shù)內(nèi)部的子函數(shù)才能讀取局部變量,因此可以把閉包簡單理解成“定義在一個函數(shù)內(nèi)部的函數(shù)”。
?
所以,在本質(zhì)上,閉包就是將函數(shù)內(nèi)部和函數(shù)外部連接起來的一座橋梁。
?
--------------------------------------------------------------------------------------------------------b
?
四、閉包的用途
?
閉包可以用在許多地方。它的最大用處有兩個,一個是前面提到的可以讀取函數(shù)內(nèi)部的變量,另一個就是讓這些變量的值始終保持在內(nèi)存中。
?
怎么來理解這句話呢?請看下面的代碼。
?
?
Js代碼
?
function f1(){
?
var n=999;
?
nAdd=function(){n+=1}
?
function f2(){
alert(n);
}
?
return f2;
?
}
?
var result=f1();
?
result(); // 999
?
nAdd();
?
result(); // 1000
?
在這段代碼中,result實際上就是閉包f2函數(shù)。它一共運行了兩次,第一次的值是999,第二次的值是1000。這證明了,函數(shù)f1中的局部變量n一直保存在內(nèi)存中,并沒有在f1調(diào)用后被自動清除。
?
為什么會這樣呢?原因就在于f1是f2的父函數(shù),而f2被賦給了一個全局變量,這導致f2始終在內(nèi)存中,而f2的存在依賴于f1,因此f1也始終在內(nèi)存中,不會在調(diào)用結(jié)束后,被垃圾回收機制(garbage collection)回收。
?
這段代碼中另一個值得注意的地方,就是“nAdd=function(){n+=1}”這一行,首先在nAdd前面沒有使用var關(guān)鍵字,因此 nAdd是一個全局變量,而不是局部變量。其次,nAdd的值是一個匿名函數(shù)(anonymous function),而這個
?
匿名函數(shù)本身也是一個閉包,所以nAdd相當于是一個setter,可以在函數(shù)外部對函數(shù)內(nèi)部的局部變量進行操作。
?
--------------------------------------------------------------------------------------------------------
?
五、使用閉包的注意點
?
1)由于閉包會使得函數(shù)中的變量都被保存在內(nèi)存中,內(nèi)存消耗很大,所以不能濫用閉包,否則會造成網(wǎng)頁的性能問題,在IE中可能導致內(nèi)存泄露。解決方法是,在退出函數(shù)之前,將不使用的局部變量全部刪除。
?
2)閉包會在父函數(shù)外部,改變父函數(shù)內(nèi)部變量的值。所以,如果你把父函數(shù)當作對象(object)使用,把閉包當作它的公用方法(Public Method),把內(nèi)部變量當作它的私有屬性(private value),這時一定要小心,不要隨便
?
改變父函數(shù)內(nèi)部變量的值。
?
--------------------------------------------------------------------------------------------------------
?
六、思考題
?
如果你能理解下面代碼的運行結(jié)果,應該就算理解閉包的運行機制了。
?
Js代碼
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
return function(){
return this.name;
};
}
};
alert(object.getNameFunc()()); //The Window
?
--------------------------------------------------------------------------------------------------------
JavaScript閉包例子
?
function outerFun()
{
var a=0;
function innerFun()
{
a++;
alert(a);
}
}
innerFun()
?
上面的代碼是錯誤的.innerFun()的作用域在outerFun()內(nèi)部,所在outerFun()外部調(diào)用它是錯誤的.
?
改成如下,也就是閉包:
?
Js代碼
?
function outerFun()
{
var a=0;
function innerFun()
{
a++;
alert(a);
}
return innerFun; //注意這里
}
var obj=outerFun();
obj(); //結(jié)果為1
obj(); //結(jié)果為2
var obj2=outerFun();
obj2(); //結(jié)果為1
obj2(); //結(jié)果為2
?
什么是閉包:
?
當內(nèi)部函數(shù) 在定義它的作用域 的外部 被引用時,就創(chuàng)建了該內(nèi)部函數(shù)的閉包 ,如果內(nèi)部函數(shù)引用了位于外部函數(shù)的變量,當外部函數(shù)調(diào)用完畢后,這些變量在內(nèi)存不會被 釋放,因為閉包需要它們.
?
--------------------------------------------------------------------------------------------------------
?
再來看一個例子
?
Js代碼
?
function outerFun()
{
var a =0;
alert(a);
}
var a=4;
outerFun();
alert(a);
?
結(jié)果是 0,4 . 因為在函數(shù)內(nèi)部使用了var關(guān)鍵字 維護a的作用域在outFun()內(nèi)部.
?
再看下面的代碼:
?
Js代碼
?
function outerFun()
{
//沒有var
a =0;
alert(a);
}
var a=4;
outerFun();
alert(a);
結(jié)果為 0,0 真是奇怪,為什么呢?
?
作用域鏈是描述一種路徑的術(shù)語,沿著該路徑可以確定變量的值 .當執(zhí)行a=0時,因為沒有使用var關(guān)鍵字,因此賦值操作會沿著作用域鏈到var a=4; 并改變其值.
?
--------------------------------------------------------------------------------------------------------------------------------------------------
?
?
如果你對javascript閉包還不是很理解,那么請看下面轉(zhuǎn)載的文章:(轉(zhuǎn)載:http://www.felixwoo.com/archives/247)
?
?
?
一、什么是閉包?
?
官方”的解釋是:閉包是一個擁有許多變量和綁定了這些變量的環(huán)境的表達式(通常是一個函數(shù)),因而這些變量也是該表達式的一部分。
相信很少有人能直接看懂這句話,因為他描述的太學術(shù)。其實這句話通俗的來說就是:JavaScript中所有的function都是一個閉包。不過一般來說,嵌套的function所產(chǎn)生的閉包更為強大,也是大部分時候我們所謂的“閉包”。看下面這段代碼:
?
function a() {? var i = 0;? function b() { alert(++i); }? return b;}var c = a();c();這段代碼有兩個特點:
?
1、函數(shù)b嵌套在函數(shù)a內(nèi)部;
?
2、函數(shù)a返回函數(shù)b。
?
引用關(guān)系如圖:
?
?
?
這樣在執(zhí)行完var c=a()后,變量c實際上是指向了函數(shù)b,再執(zhí)行c()后就會彈出一個窗口顯示i的值(第一次為1)。這段代碼其實就創(chuàng)建了一個閉包,為什么?因為函數(shù)a外的變量c引用了函數(shù)a內(nèi)的函數(shù)b,就是說:
?
當函數(shù)a的內(nèi)部函數(shù)b被函數(shù)a外的一個變量引用的時候,就創(chuàng)建了一個閉包。
?
?
讓我們說的更透徹一些。所謂“閉包”,就是在構(gòu)造函數(shù)體內(nèi)定義另外的函數(shù)作為目標對象的方法函數(shù),而這個對象的方法函數(shù)反過來引用外層函數(shù)體中的臨時變量。這使得只要目標 對象在生存期內(nèi)始終能保持其方法,就能間接保持原構(gòu)造函數(shù)體當時用到的臨時變量值。盡管最開始的構(gòu)造函數(shù)調(diào)用已經(jīng)結(jié)束,臨時變量的名稱也都消失了,但在目 標對象的方法內(nèi)卻始終能引用到該變量的值,而且該值只能通這種方法來訪問。即使再次調(diào)用相同的構(gòu)造函數(shù),但只會生成新對象和方法,新的臨時變量只是對應新 的值,和上次那次調(diào)用的是各自獨立的。
?
二、閉包有什么作用?
?
?
簡而言之,閉包的作用就是在a執(zhí)行完并返回后,閉包使得Javascript的垃圾回收機制GC不會收回a所占用的資源,因為a的內(nèi)部函數(shù)b的執(zhí)行需要依賴a中的變量。這是對閉包作用的非常直白的描述,不專業(yè)也不嚴謹,但大概意思就是這樣,理解閉包需要循序漸進的過程。
?
在上面的例子中,由于閉包的存在使得函數(shù)a返回后,a中的i始終存在,這樣每次執(zhí)行c(),i都是自加1后alert出i的值。
?
那 么我們來想象另一種情況,如果a返回的不是函數(shù)b,情況就完全不同了。因為a執(zhí)行完后,b沒有被返回給a的外界,只是被a所引用,而此時a也只會被b引 用,因此函數(shù)a和b互相引用但又不被外界打擾(被外界引用),函數(shù)a和b就會被GC回收。(關(guān)于Javascript的垃圾回收機制將在后面詳細介紹)
?
三、閉包內(nèi)的微觀世界
?
如果要更加深入的了解閉包以及函數(shù)a和嵌套函數(shù)b的關(guān)系,我們需要引入另外幾個概念:函數(shù)的執(zhí)行環(huán)境(excution context)、活動對象(call object)、作用域(scope)、作用域鏈(scope chain)。以函數(shù)a從定義到執(zhí)行的過程為例闡述這幾個概念。
?
當定義函數(shù)a的時候,js解釋器會將函數(shù)a的作用域鏈(scope chain)設置為定義a時a所在的“環(huán)境”,如果a是一個全局函數(shù),則scope chain中只有window對象。
當執(zhí)行函數(shù)a的時候,a會進入相應的執(zhí)行環(huán)境(excution context)。
在創(chuàng)建執(zhí)行環(huán)境的過程中,首先會為a添加一個scope屬性,即a的作用域,其值就為第1步中的scope chain。即a.scope=a的作用域鏈。
然后執(zhí)行環(huán)境會創(chuàng)建一個活動對象(call object)。活動對象也是一個擁有屬性的對象,但它不具有原型而且不能通過JavaScript代碼直接訪問。創(chuàng)建完活動對象后,把活動對象添加到a的作用域鏈的最頂端。此時a的作用域鏈包含了兩個對象:a的活動對象和window對象。
下一步是在活動對象上添加一個arguments屬性,它保存著調(diào)用函數(shù)a時所傳遞的參數(shù)。
最后把所有函數(shù)a的形參和內(nèi)部的函數(shù)b的引用也添加到a的活動對象上。在這一步中,完成了函數(shù)b的的定義,因此如同第3步,函數(shù)b的作用域鏈被設置為b所被定義的環(huán)境,即a的作用域。
到此,整個函數(shù)a從定義到執(zhí)行的步驟就完成了。此時a返回函數(shù)b的引用給c,又函數(shù)b的作用域鏈包含了對函數(shù)a的活動對象的引用,也就是說b可以訪問到a中定義的所有變量和函數(shù)。函數(shù)b被c引用,函數(shù)b又依賴函數(shù)a,因此函數(shù)a在返回后不會被GC回收。
?
當函數(shù)b執(zhí)行的時候亦會像以上步驟一樣。因此,執(zhí)行時b的作用域鏈包含了3個對象:b的活動對象、a的活動對象和window對象,如下圖所示:
?
?
?
如圖所示,當在函數(shù)b中訪問一個變量的時候,搜索順序是:
?
先搜索自身的活動對象,如果存在則返回,如果不存在將繼續(xù)搜索函數(shù)a的活動對象,依次查找,直到找到為止。
如果函數(shù)b存在prototype原型對象,則在查找完自身的活動對象后先查找自身的原型對象,再繼續(xù)查找。這就是Javascript中的變量查找機制。
如果整個作用域鏈上都無法找到,則返回undefined。
小結(jié),本段中提到了兩個重要的詞語:函數(shù)的定義與執(zhí)行。文中提到函數(shù)的作用域是在定義函數(shù)時候就已經(jīng)確定,而不是在執(zhí)行的時候確定(參看步驟1和3)。用一段代碼來說明這個問題:
?
function f(x) {?? var g = function () { return x; }? return g;}var h = f(1);alert(h()); 這段代碼中變量h指向了f中的那個匿名函數(shù)(由g返回)。
?
假設函數(shù)h的作用域是在執(zhí)行alert(h())確定的,那么此時h的作用域鏈是:h的活動對象->alert的活動對象->window對象。
假設函數(shù)h的作用域是在定義時確定的,就是說h指向的那個匿名函數(shù)在定義的時候就已經(jīng)確定了作用域。那么在執(zhí)行的時候,h的作用域鏈為:h的活動對象->f的活動對象->window對象。
如果第一種假設成立,那輸出值就是undefined;如果第二種假設成立,輸出值則為1。
?
運行結(jié)果證明了第2個假設是正確的,說明函數(shù)的作用域確實是在定義這個函數(shù)的時候就已經(jīng)確定了。
?
?
?
四、閉包的應用場景
保護函數(shù)內(nèi)的變量安全。以最開始的例子為例,函數(shù)a中i只有函數(shù)b才能訪問,而無法通過其他途徑訪問到,因此保護了i的安全性。
?
在內(nèi)存中維持一個變量。依然如前例,由于閉包,函數(shù)a中i的一直存在于內(nèi)存中,因此每次執(zhí)行c(),都會給i自加1。
通過保護變量的安全實現(xiàn)JS私有屬性和私有方法(不能被外部訪問)
私有屬性和方法在Constructor外是無法被訪問的
function Constructor(...) {
var that = this;
var membername = value;
function membername(...) {...}
}
?
以上3點是閉包最基本的應用場景,很多經(jīng)典案例都源于此。
?
?
?
五、Javascript的垃圾回收機制
?
?
在Javascript中,如果一個對象不再被引用,那么這個對象就會被GC回收。如果兩個對象互相引用,而不再被第3者所引用,那么這兩個互相引用的對象也會被回收。因為函數(shù)a被b引用,b又被a外的c引用,這就是為什么函數(shù)a執(zhí)行后不會被回收的原因。
?
?
六、結(jié)語
?
理解JavaScript的閉包是邁向高級JS程序員的必經(jīng)之路,理解了其解釋和運行機制才能寫出更為安全和優(yōu)雅的代碼。
轉(zhuǎn)載于:https://www.cnblogs.com/08291018wan/p/5757657.html
總結(jié)
以上是生活随笔為你收集整理的深入理解JavaScript的闭包特性如何给循环中的对象添加事件的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java .Net Byte数组存储差异
- 下一篇: Spring boot maven 搭