tr闭包_嵌套函数及闭包
這篇文章其實是要講閉包的一些初級應(yīng)用,但是為了將閉包,我們還是從嵌套函數(shù)開始說吧,縱使所有的JavaScript函數(shù)都可以說是閉包,但是只有當(dāng)一個嵌套函數(shù)被導(dǎo)出到它所定義的作用域之外時,這種閉包才是有趣的。
【嵌套函數(shù)】
JavaScript允許嵌入的函數(shù),允許函數(shù)用作數(shù)據(jù),并且在函數(shù)詞法作用域下面,可以產(chǎn)生與傳統(tǒng)面向?qū)ο笳Z言不同的驚人地方。
首先,JavaScript的函數(shù)是通過詞法來劃分作用域的,而不是動態(tài)的劃分作用域的,于是,函數(shù)的是在定義它們的作用域中運行,而不是在執(zhí)行它們的作用域中運行,所以,當(dāng)嵌套函數(shù)和它的外圍函數(shù)定義在同一個詞法作用域中的時候,是很容易理解的。比如下面很平淡無奇的代碼:
varx='global';functionf () {varx='local';functiong() {
alert(x);
}
g();
}
f();//'local'
當(dāng)f()調(diào)用的時候,作用域鏈可以理解為由兩部分組成,包含f這一調(diào)用的調(diào)用對象,然后后面是全局對象。此時查找x的值,會先從f的調(diào)用對象中查找,如果沒有,再查找后面全局對象中x。同理,g因為是f的一個嵌套函數(shù),那么,g調(diào)用的時候,作用域鏈應(yīng)該就是由三部分組成了,g的調(diào)用對象,f的調(diào)用對象,和全局對象。函數(shù)g是要輸出x的值,所以會先在g的調(diào)用對象中查找x的值,g中沒有定義,接下來查找外圍f調(diào)用對象中x的定義,于是找到了x='local',那么就會輸出x,而不會繼續(xù)往下查找全局對象了。 ?如果f中也沒定義x的值,那么就會繼續(xù)查找作用域鏈后面的全局對象,結(jié)果就是global了。如果全局對象中也沒定義,那么自然就是undefined。
好了,我們對作用域鏈有了個初步的理解,同時我們知道,閉包有兩個比較常用的用途,一個是可以利用它訪問到局部變量,另一個是可以把它外圍作用域中的變量值存儲在內(nèi)存中而不在函數(shù)調(diào)用完畢后就銷毀。
下面接著看一個平淡無奇的例子,或許可以幫助理解為什么閉包可以把外部變量值保存在內(nèi)存中了。
functionmakeFunc (x) {returnfunction() {returnx++}
}vara=[makeFunc(0), makeFunc(1), makeFunc(2)];
alert(a[0]());
alert(a[1]());
alert(a[2]());
執(zhí)行結(jié)果為0,1,2 ;也沒有什么特別的地方,這也是嚴(yán)格的詞法作用域的正常表現(xiàn)。每次makeFunc調(diào)用完畢后,它的調(diào)用對象會從作用域鏈中移除,再沒有任何對它的引用,最終通過垃圾收集而完結(jié)。說的詳細(xì)一點,我們可以這樣理解。
makeFunc每次調(diào)用的時候,會為他創(chuàng)建一個調(diào)用對象放置到作用域鏈中。針對makeFunc這個函數(shù)而言,這個調(diào)用對象包含一個屬性x(也就是函數(shù)的參數(shù),因為函數(shù)參數(shù)可以看做調(diào)用對象的一個屬性),makeFunc會返回一個匿名嵌套函數(shù)的引用,接下來這個匿名嵌套函數(shù)執(zhí)行,又會創(chuàng)建一個調(diào)用對象,放置到作用域鏈中,匿名函數(shù)返回x的值,(注意:匿名函數(shù)的調(diào)用對象中是沒有x的定義的,于是它會引用到它外圍的函數(shù)makeFunc的調(diào)用對象,訪問到x)然后x加1,至此,匿名函數(shù)執(zhí)行完畢,它調(diào)用對象從作用域鏈中移除,?然后makeFunc也執(zhí)行完畢,makeFunc調(diào)用對象也被移除。由于它的調(diào)用對象中包含x,所以x也隨著它的銷毀而銷毀。不會保存下來。
以上就是函數(shù)的詳細(xì)的執(zhí)行過程,請仔細(xì)理解后看看下面改動的代碼:
varx=0;functionmakeFunc () {returnfunction() {returnx++}
}vara=[makeFunc(), makeFunc(), makeFunc()];
alert(a[0]());
alert(a[1]());
alert(a[2]());
現(xiàn)在x是一個全局變量了,執(zhí)行結(jié)果為0,1,2;但是這個結(jié)果就與上面的有些不同了。下面我們還是從作用域鏈的方向來理解這個結(jié)果產(chǎn)生的原因。
同樣,makeFunc每次調(diào)用的時候會創(chuàng)建一個調(diào)用對象到作用域鏈中,由于它返回內(nèi)部嵌套函數(shù)的引用,所以內(nèi)部嵌套函數(shù)開始執(zhí)行,又創(chuàng)建一個嵌套函數(shù)的調(diào)用對象到作用域鏈。然后返回x的值,注意,這里就不同了,嵌套函數(shù)的調(diào)用對象中沒有x,它外圍的makeFunc的調(diào)用對象中也沒有x,只能接著往下查找到全局對象中,在全局對象中找到了x的定義,于是正常執(zhí)行,返回x的值,x加1,然后嵌套函數(shù)完畢,調(diào)用對象移除,接著makeFunc完畢,調(diào)用對象也移除,可是因為他們的調(diào)用對象中都沒有x,他們的調(diào)用對象銷毀根本不會影響到x。于是,全局變量x值的改變就這樣被保存下來了。
注意,上面說的訪問外圍的調(diào)用對象只是為了幫助理解而不嚴(yán)格的說法,JavaScript不會以任何方式直接訪問調(diào)用對象,但是,它定義的屬性作為調(diào)用對象中作用域鏈的一部分,還是“活的”。另外,如果一個外圍函數(shù)包含了兩個或多個嵌套函數(shù)都對全局對象有引用,那么這些嵌套函數(shù)都共享同一個全局調(diào)用對象,并且其中一個對全局對象的改變對其他的都是可見的。
好了,在JavaScript里,函數(shù)是將要執(zhí)行的代碼以及執(zhí)行這些代碼的作用域構(gòu)成的一個綜合體,廣義的說,我們就可以把這種代碼和作用域的綜合體叫做閉包。
【閉包】
我們偶爾需要寫一個需要通過調(diào)用來記住一個變量值的函數(shù)。于是,如果我們了解了作用域,就會知道,局部變量是很難做到的,因為函數(shù)的調(diào)用對象不能在調(diào)用后一直維持。全局變量可以做到,就如上面的例子一樣,但是這樣很容易造成全局變量污染。既然調(diào)用對象不能維持,那么我們不把值保存在調(diào)用對象中不就行了?!所以,下面是實現(xiàn)的一種方法:用函數(shù)對象自身的屬性來保存。
uniqueID=function() {if(!arguments.callee.id) arguments.callee.id=0;returnarguments.callee.id++;
}
alert(uniqueID());//0alert(uniqueID());//1
如上,因為函數(shù)本身就是一個對象,所以,我們用它自身的一個屬性來保存是可行的,但是這樣做有一個問題,就是任何人在任何時候都可以通過unqueID.id強制訪問到我們原本保存到的值并作出修改。這是我們不愿看到的。
所以,通常,我們使用閉包來實現(xiàn)這件事。如下:
_uniqueID=(function(){varid=0;returnfunction() {returnid++}
})();
alert(_uniqueID());//0alert(_uniqueID());//1
同樣,我們也用作用鏈域來解釋下結(jié)果。注意到_uniqueID本身就是一個匿名函數(shù),它內(nèi)部又有個匿名嵌套函數(shù),我們直接調(diào)用的是_uniqueID(),也就是說,我們直接調(diào)用的其實是_uniqueID內(nèi)部的嵌套函數(shù),而它本身的調(diào)用對象沒有定義id,于是引用外圍的調(diào)用對象中的id,并返回,id加1,執(zhí)行完畢,內(nèi)層嵌套函數(shù)調(diào)用對象移出作用域鏈。而外圍的id并沒有被銷毀,于是就這樣保存了下來。
有人可能會疑惑,不是說調(diào)用對象在函數(shù)執(zhí)行完畢后就移除了作用域鏈嗎,外圍匿名函數(shù)(function(){})();也是調(diào)用完畢了的,應(yīng)該調(diào)用對象也沒了才對。
是的,調(diào)用對象是在當(dāng)前函數(shù)執(zhí)行完畢后就結(jié)束引用,但是這里不要誤解了上面_uniqueID()的調(diào)用,他并不是直接調(diào)用的外圍函數(shù),而是調(diào)用的嵌套函數(shù),嵌套函數(shù)的作用域鏈?zhǔn)前鈬瘮?shù)的作用域鏈的。所以在它的調(diào)用對象移除作用域鏈的時候是能夠訪問到這條作用域鏈上其他對象的屬性并改變的。
閉包本身就是個難以理解但是又非常有用的東西,希望能對有需要的人一些幫助吧。此外,資歷所限,本人理解也可能有誤,如發(fā)現(xiàn),敬請指正。
來源:?http://www.cnblogs.com/hongru/archive/2010/10/30/1865041.html
總結(jié)
以上是生活随笔為你收集整理的tr闭包_嵌套函数及闭包的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 前三个月胎停育有什么症状
- 下一篇: 成都大熊猫基地能带吃的进去吗