在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ HTML/ 淺談 Javascript 嵌套函數(shù)及閉包
淺談 JavaScript 之事件綁定
淺談 javascript 中字符串 String 與數(shù)組 Array
淺談 javascript 中基本包裝類型
淺談 JavaScript Math 和 Number 對(duì)象
淺談 Javascript 的靜態(tài)屬性和原型屬性
淺談 JavaScript 中定義變量時(shí)有無(wú) var 聲明的區(qū)別
淺談 JavaScript Array 對(duì)象
淺談 JavaScript 函數(shù)參數(shù)的可修改性問(wèn)題
淺談 javascript 中的 instanceof 和 typeof
淺談 JavaScript 中 Date (日期對(duì)象),Math 對(duì)象
淺談 Javascript 執(zhí)行順序
淺談 javascript 函數(shù)屬性和方法
淺談 JavaScript 中面向?qū)ο蠹夹g(shù)的模擬
淺談 javascript 的原型繼承
淺談 javascript 事件取消和阻止冒泡
根據(jù)一段代碼淺談 Javascript 閉包
淺談 Javascript 面向?qū)ο缶幊?/span>
淺談 javascript 六種數(shù)據(jù)類型以及特殊注意點(diǎn)
淺談 Javascript 變量作用域問(wèn)題
淺談 javascript 函數(shù)內(nèi)部屬性
淺談 javascript 中自定義模版
淺談 JavaScript 字符集
淺談 javascript 面向?qū)ο缶幊?/span>
淺談 JavaScript 框架分類
淺談 JavaScript 中的 Math.atan() 方法的使用
淺談 Javascript 數(shù)組與字典
淺談 JavaScript 數(shù)據(jù)類型及轉(zhuǎn)換
淺談 javascript 的調(diào)試
淺談 Javascript 嵌套函數(shù)及閉包
淺談 javascript 回調(diào)函數(shù)
淺談 JavaScript Date 日期和時(shí)間對(duì)象
淺談 Javascript 中的 Function 與 Object
淺談 JavaScript 數(shù)據(jù)類型
淺談 javascript 中 this 在事件中的應(yīng)用
淺談 javascript 中的閉包
淺談 javascript 函數(shù)劫持
淺談 Javascript 中深復(fù)制
淺談 JavaScript 函數(shù)節(jié)流
淺談 JavaScript 中的 String 對(duì)象常用方法
淺談 JavaScript 事件的屬性列表
淺談 JavaScript 函數(shù)與棧
淺談 JavaScript 的事件
淺談 javascript 中的作用域
淺談 JavaScript 的執(zhí)行效率
淺談 Javascript 事件模擬
淺談 JavaScript function 函數(shù)種類
淺談 javascript 歸并方法
淺談 javascript 迭代方法
淺談 JavaScript 編程語(yǔ)言的編碼規(guī)范
淺談 JavaScript 實(shí)現(xiàn)面向?qū)ο笾械念?/span>
淺談 Javascript 鼠標(biāo)和滾輪事件
淺談 Javascript Base64 加密解密
淺談 Javascript 中勻速運(yùn)動(dòng)的停止條件
淺談 javascript 實(shí)現(xiàn)八大排序
淺談 javascript 的分號(hào)的使用
淺談 javascript 中 createElement 事件
淺談 javascript 的數(shù)據(jù)類型檢測(cè)
淺談 javascript 對(duì)象模型和 function 對(duì)象
淺談 Javascript 如何實(shí)現(xiàn)勻速運(yùn)動(dòng)
淺談 JavaScript 字符串與數(shù)組
淺談 javascript 面向?qū)ο蟪绦蛟O(shè)計(jì)
淺談 Javascript 事件處理程序的幾種方式

淺談 Javascript 嵌套函數(shù)及閉包

嵌套函數(shù)

JavaScript 允許嵌入的函數(shù),允許函數(shù)用作數(shù)據(jù),并且在函數(shù)詞法作用域下面,可以產(chǎn)生與傳統(tǒng)面向?qū)ο笳Z(yǔ)言不同的驚人地方。

首先,JavaScript 的函數(shù)是通過(guò)詞法來(lái)劃分作用域的,而不是動(dòng)態(tài)的劃分作用域的,于是,函數(shù)的是在定義它們的作用域中運(yùn)行,而不是在執(zhí)行它們的作用域中運(yùn)行,所以,當(dāng)嵌套函數(shù)和它的外圍函數(shù)定義在同一個(gè)詞法作用域中的時(shí)候,是很容易理解的。比如下面很平淡無(wú)奇的代碼:

var x = 'global';   
function f () {   
var x = 'local';   
function g() {   
alert(x);   
}   
g();   
}   
f(); // 'local'  

當(dāng) f()調(diào)用的時(shí)候,作用域鏈可以理解為由兩部分組成,包含 f 這一調(diào)用的調(diào)用對(duì)象,然后后面是全局對(duì)象。此時(shí)查找 x 的值,會(huì)先從 f 的調(diào)用對(duì)象中查找,如果沒(méi)有,再查找后面全局對(duì)象中 x。同理,g 因?yàn)槭?f 的一個(gè)嵌套函數(shù),那么,g 調(diào)用的時(shí)候,作用域鏈應(yīng)該就是由三部分組成了,g 的調(diào)用對(duì)象,f 的調(diào)用對(duì)象,和全局對(duì)象。函數(shù) g 是要輸出 x 的值,所以會(huì)先在 g 的調(diào)用對(duì)象中查找 x 的值,g 中沒(méi)有定義,接下來(lái)查找外圍 f 調(diào)用對(duì)象中 x 的定義,于是找到了 x='local',那么就會(huì)輸出 x,而不會(huì)繼續(xù)往下查找全局對(duì)象了。 如果 f 中也沒(méi)定義 x 的值,那么就會(huì)繼續(xù)查找作用域鏈后面的全局對(duì)象,結(jié)果就是 global 了。如果全局對(duì)象中也沒(méi)定義,那么自然就是 undefined。

好了,我們對(duì)作用域鏈有了個(gè)初步的理解,同時(shí)我們知道,閉包有兩個(gè)比較常用的用途,一個(gè)是可以利用它訪問(wèn)到局部變量,另一個(gè)是可以把它外圍作用域中的變量值存儲(chǔ)在內(nèi)存中而不在函數(shù)調(diào)用完畢后就銷毀。

下面接著看一個(gè)平淡無(wú)奇的例子,或許可以幫助理解為什么閉包可以把外部變量值保存在內(nèi)存中了。

function makeFunc (x) {   
return function () {return x++}   
}   
var a = [makeFunc(0), makeFunc(1), makeFunc(2)];   
alert(a[0]());   
alert(a[1]());   
alert(a[2]());  

執(zhí)行結(jié)果為 0,,1,2;也沒(méi)有什么特別的地方,這也是嚴(yán)格的詞法作用域的正常表現(xiàn)。每次 makeFunc 調(diào)用完畢后,它的調(diào)用對(duì)象會(huì)從作用域鏈中移除,再?zèng)]有任何對(duì)它的引用,最終通過(guò)垃圾收集而完結(jié)。說(shuō)的詳細(xì)一點(diǎn),我們可以這樣理解。

makeFunc 每次調(diào)用的時(shí)候,會(huì)為他創(chuàng)建一個(gè)調(diào)用對(duì)象放置到作用域鏈中。針對(duì) makeFunc 這個(gè)函數(shù)而言,這個(gè)調(diào)用對(duì)象包含一個(gè)屬性 x(也就是函數(shù)的參數(shù),因?yàn)楹瘮?shù)參數(shù)可以看做調(diào)用對(duì)象的一個(gè)屬性),makeFunc 會(huì)返回一個(gè)匿名嵌套函數(shù)的引用,接下來(lái)這個(gè)匿名嵌套函數(shù)執(zhí)行,又會(huì)創(chuàng)建一個(gè)調(diào)用對(duì)象,放置到作用域鏈中,匿名函數(shù)返回 x 的值,(注意:匿名函數(shù)的調(diào)用對(duì)象中是沒(méi)有 x 的定義的,于是它會(huì)引用到它外圍的函數(shù) makeFunc 的調(diào)用對(duì)象,訪問(wèn)到 x)然后 x 加 1,至此,匿名函數(shù)執(zhí)行完畢,它調(diào)用對(duì)象從作用域鏈中移除, 然后 makeFunc 也執(zhí)行完畢,makeFunc 調(diào)用對(duì)象也被移除。由于它的調(diào)用對(duì)象中包含 x,所以 x 也隨著它的銷毀而銷毀。不會(huì)保存下來(lái)。

以上就是函數(shù)的詳細(xì)的執(zhí)行過(guò)程,請(qǐng)仔細(xì)理解后看看下面改動(dòng)的代碼:

var x = 0;   
function makeFunc () {   
return function () {return x++}   
}   
var a = [makeFunc(), makeFunc(), makeFunc()];   
alert(a[0]());   
alert(a[1]());   
alert(a[2]());  

現(xiàn)在 x 是一個(gè)全局變量了,執(zhí)行結(jié)果為 0,1,2;但是這個(gè)結(jié)果就與上面的有些不同了。下面我們還是從作用域鏈的方向來(lái)理解這個(gè)結(jié)果產(chǎn)生的原因。

同樣,makeFunc 每次調(diào)用的時(shí)候會(huì)創(chuàng)建一個(gè)調(diào)用對(duì)象到作用域鏈中,由于它返回內(nèi)部嵌套函數(shù)的引用,所以內(nèi)部嵌套函數(shù)開(kāi)始執(zhí)行,又創(chuàng)建一個(gè)嵌套函數(shù)的調(diào)用對(duì)象到作用域鏈。然后返回 x 的值,注意,這里就不同了,嵌套函數(shù)的調(diào)用對(duì)象中沒(méi)有 x,它外圍的 makeFunc 的調(diào)用對(duì)象中也沒(méi)有 x,只能接著往下查找到全局對(duì)象中,在全局對(duì)象中找到了 x 的定義,于是正常執(zhí)行,返回 x 的值,x 加 1,然后嵌套函數(shù)完畢,調(diào)用對(duì)象移除,接著 makeFunc 完畢,調(diào)用對(duì)象也移除,可是因?yàn)樗麄兊恼{(diào)用對(duì)象中都沒(méi)有 x,他們的調(diào)用對(duì)象銷毀根本不會(huì)影響到 x。于是,全局變量 x 值的改變就這樣被保存下來(lái)了。

注意,上面說(shuō)的訪問(wèn)外圍的調(diào)用對(duì)象只是為了幫助理解而不嚴(yán)格的說(shuō)法,JavaScript 不會(huì)以任何方式直接訪問(wèn)調(diào)用對(duì)象,但是,它定義的屬性作為調(diào)用對(duì)象中作用域鏈的一部分,還是 “活的”。另外,如果一個(gè)外圍函數(shù)包含了兩個(gè)或多個(gè)嵌套函數(shù)都對(duì)全局對(duì)象有引用,那么這些嵌套函數(shù)都共享同一個(gè)全局調(diào)用對(duì)象,并且其中一個(gè)對(duì)全局對(duì)象的改變對(duì)其他的都是可見(jiàn)的。

好了,在 JavaScript 里,函數(shù)是將要執(zhí)行的代碼以及執(zhí)行這些代碼的作用域構(gòu)成的一個(gè)綜合體,廣義的說(shuō),我們就可以把這種代碼和作用域的綜合體叫做閉包。

閉包

我們偶爾需要寫(xiě)一個(gè)需要通過(guò)調(diào)用來(lái)記住一個(gè)變量值的函數(shù)。于是,如果我們了解了作用域,就會(huì)知道,局部變量是很難做到的,因?yàn)楹瘮?shù)的調(diào)用對(duì)象不能在調(diào)用后一直維持。全局變量可以做到,就如上面的例子一樣,但是這樣很容易造成全局變量污染。既然調(diào)用對(duì)象不能維持,那么我們不把值保存在調(diào)用對(duì)象中不就行了?!所以,下面是實(shí)現(xiàn)的一種方法:用函數(shù)對(duì)象自身的屬性來(lái)保存。

uniqueID = function () {   
if (!arguments.callee.id) arguments.callee.id = 0;   
return arguments.callee.id ++;   
}   
alert(uniqueID()); //0   
alert(uniqueID()); //1  

如上,因?yàn)楹瘮?shù)本身就是一個(gè)對(duì)象,所以,我們用它自身的一個(gè)屬性來(lái)保存是可行的,但是這樣做有一個(gè)問(wèn)題,就是任何人在任何時(shí)候都可以通過(guò) unqueID.id 強(qiáng)制訪問(wèn)到我們?cè)颈4娴降闹挡⒆鞒鲂薷摹_@是我們不愿看到的。

所以,通常,我們使用閉包來(lái)實(shí)現(xiàn)這件事。如下:

_uniqueID = (function(){   
var id = 0;   
return function () {return id ++}   
})();   
alert(_uniqueID()); //0   
alert(_uniqueID()); //1  

同樣,我們也用作用鏈域來(lái)解釋下結(jié)果。注意到_uniqueID 本身就是一個(gè)匿名函數(shù),它內(nèi)部又有個(gè)匿名嵌套函數(shù),我們直接調(diào)用的是_uniqueID(),也就是說(shuō),我們直接調(diào)用的其實(shí)是_uniqueID 內(nèi)部的嵌套函數(shù),而它本身的調(diào)用對(duì)象沒(méi)有定義 id,于是引用外圍的調(diào)用對(duì)象中的 id,并返回,id 加 1,執(zhí)行完畢,內(nèi)層嵌套函數(shù)調(diào)用對(duì)象移出作用域鏈。而外圍的 id 并沒(méi)有被銷毀,于是就這樣保存了下來(lái)。

有人可能會(huì)疑惑,不是說(shuō)調(diào)用對(duì)象在函數(shù)執(zhí)行完畢后就移除了作用域鏈嗎,外圍匿名函數(shù) (function(){})();也是調(diào)用完畢了的,應(yīng)該調(diào)用對(duì)象也沒(méi)了才對(duì)。

是的,調(diào)用對(duì)象是在當(dāng)前函數(shù)執(zhí)行完畢后就結(jié)束引用,但是這里不要誤解了上面_uniqueID() 的調(diào)用,他并不是直接調(diào)用的外圍函數(shù),而是調(diào)用的嵌套函數(shù),嵌套函數(shù)的作用域鏈?zhǔn)前鈬瘮?shù)的作用域鏈的。所以在它的調(diào)用對(duì)象移除作用域鏈的時(shí)候是能夠訪問(wèn)到這條作用域鏈上其他對(duì)象的屬性并改變的。

閉包本身就是個(gè)難以理解但是又非常有用的東西,希望能對(duì)有需要的人一些幫助吧。此外,資歷所限,本人理解也可能有誤,如發(fā)現(xiàn),敬請(qǐng)指正。