欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

前端開(kāi)發(fā)必須知道的JS之閉包及應(yīng)用

 更新時(shí)間:2010年07月06日 12:58:33   作者:  
本文講的是函數(shù)閉包,不涉及對(duì)象閉包(如用with實(shí)現(xiàn))。如果你覺(jué)得我說(shuō)的有偏差,歡迎拍磚,歡迎指教。
在前端開(kāi)發(fā)必須知道的JS之原型和繼承一文中說(shuō)過(guò)下面寫篇閉包,加之最近越來(lái)越發(fā)現(xiàn)需要加強(qiáng)我的閉包應(yīng)用能力,所以此文不能再拖了。本文講的是函數(shù)閉包,不涉及對(duì)象閉包(如用with實(shí)現(xiàn))。如果你覺(jué)得我說(shuō)的有偏差,歡迎拍磚,歡迎指教。
一. 閉包的理論
  首先必須了解以下幾個(gè)概念:

  執(zhí)行環(huán)境
  每調(diào)用一個(gè)函數(shù)時(shí)(執(zhí)行函數(shù)時(shí)),系統(tǒng)會(huì)為該函數(shù)創(chuàng)建一個(gè)封閉的局部的運(yùn)行環(huán)境,即該函數(shù)的執(zhí)行環(huán)境。函數(shù)總是在自己的執(zhí)行環(huán)境中執(zhí)行,如讀寫局部變量、函數(shù)參數(shù)、運(yùn)行內(nèi)部邏輯。創(chuàng)建執(zhí)行環(huán)境的過(guò)程包含了創(chuàng)建函數(shù)的作用域,函數(shù)也是在自己的作用域下執(zhí)行的。從另一個(gè)角度說(shuō),每個(gè)函數(shù)執(zhí)行環(huán)境都有一個(gè)作用域鏈,子函數(shù)的作用域鏈包括它的父函數(shù)的作用域鏈。關(guān)于作用域、作用域鏈請(qǐng)看下面。

  作用域、作用域鏈、調(diào)用對(duì)象
  函數(shù)作用域分為詞法作用域和動(dòng)態(tài)作用域。
  詞法作用域是函數(shù)定義時(shí)的作用域,即靜態(tài)作用域。當(dāng)一個(gè)函數(shù)定義時(shí),他的詞法作用域就確定了,詞法作用域說(shuō)明的是在函數(shù)結(jié)構(gòu)的嵌套關(guān)系下,函數(shù)作用的范圍。這個(gè)時(shí)候也就形成了該函數(shù)的作用域鏈。作用域鏈就是把這些具有嵌套層級(jí)關(guān)系的作用域串聯(lián)起來(lái)。函數(shù)的內(nèi)部[[scope]]屬性指向了該作用域鏈。
  動(dòng)態(tài)作用域是函數(shù)調(diào)用執(zhí)行時(shí)的作用域。當(dāng)一個(gè)函數(shù)被調(diào)用時(shí),首先將函數(shù)內(nèi)部[[scope]]屬性指向了函數(shù)的作用域鏈,然后會(huì)創(chuàng)建一個(gè)調(diào)用對(duì)象,并用該調(diào)用對(duì)象記錄函數(shù)參數(shù)和函數(shù)的局部變量,將其置于作用域鏈頂部。動(dòng)態(tài)作用域就是通過(guò)把該調(diào)用對(duì)象加到作用域鏈的頂部來(lái)創(chuàng)建的,此時(shí)的[[scope]]除了具有定義時(shí)的作用域鏈,還具有了調(diào)用時(shí)創(chuàng)建的調(diào)用對(duì)象。換句話說(shuō),執(zhí)行環(huán)境下的作用域等于該函數(shù)定義時(shí)就確定的作用域鏈加上該函數(shù)剛剛創(chuàng)建的調(diào)用對(duì)象,從而也形成了新的作用域鏈。所以說(shuō)是動(dòng)態(tài)的作用域,并且作用域鏈也隨之發(fā)生了變化。再看這里的作用域,其實(shí)是一個(gè)對(duì)象鏈,這些對(duì)象就是函數(shù)調(diào)用時(shí)創(chuàng)建的調(diào)用對(duì)象,以及他上面一層層的調(diào)用對(duì)象直到最上層的全局對(duì)象。 
  譬如全局環(huán)境下的函數(shù)A內(nèi)嵌套了一個(gè)函數(shù)B,則該函數(shù)B的作用域鏈就是:函數(shù)B的作用域—>函數(shù)A的作用域—>全局window的作用域。當(dāng)函數(shù)B調(diào)用時(shí),尋找某標(biāo)識(shí)符,會(huì)按函數(shù)B的作用域—>函數(shù)A的作用域—>全局window的作用域去尋找,實(shí)際上是按函數(shù)B的調(diào)用對(duì)象—>函數(shù)A的調(diào)用對(duì)象—>全局對(duì)象這個(gè)順序去尋找的。也就是說(shuō)當(dāng)函數(shù)調(diào)用時(shí),函數(shù)的作用域鏈實(shí)際上是調(diào)用對(duì)象鏈。

  閉包
  在動(dòng)態(tài)執(zhí)行環(huán)境中,數(shù)據(jù)實(shí)時(shí)地發(fā)生變化,為了保持這些非持久型變量的值,我們用閉包這種載體來(lái)存儲(chǔ)這些動(dòng)態(tài)數(shù)據(jù)(看完下面的應(yīng)用就會(huì)很好的體會(huì)這句話)。閉包的定義:所謂“閉包”,指的是一個(gè)擁有許多變量和綁定了這些變量的環(huán)境的表達(dá)式(通常是一個(gè)函數(shù)),因而這些變量也是該表達(dá)式的一部分。
  閉包就是嵌套在函數(shù)里面的內(nèi)部函數(shù),并且該內(nèi)部函數(shù)可以訪問(wèn)外部函數(shù)中聲明的所有局部變量、參數(shù)和其他內(nèi)部函數(shù)。當(dāng)該內(nèi)部函數(shù)在外部函數(shù)外被調(diào)用,就生成了閉包。(實(shí)際上任何函數(shù)都是全局作用域的內(nèi)部函數(shù),都能訪問(wèn)全局變量,所以都是window的閉包)
  譬如下面這個(gè)例子:
復(fù)制代碼 代碼如下:

<script type="text/javascript">
function f(x) {
var a = 0;
a++;
x++;
var inner = function() {
return a + x;
}
return inner;
}
var test = f(1);
alert(test());
</script>

垃圾回收機(jī)制:如果某個(gè)對(duì)象不再被引用,該對(duì)象將被回收?! ?
  再結(jié)合前面所講的一些概念,在執(zhí)行var test=f(1)時(shí)創(chuàng)建了f的調(diào)用對(duì)象,這里暫且記作obj,執(zhí)行完后雖然退出了外部執(zhí)行環(huán)境,但內(nèi)部函數(shù)inner被外部函數(shù)f外面的一個(gè)變量test引用。由于外部函數(shù)創(chuàng)建的調(diào)用對(duì)象obj有一個(gè)屬性指向此內(nèi)部函數(shù),而現(xiàn)在這個(gè)內(nèi)部函數(shù)又被引用,所以調(diào)用對(duì)象obj會(huì)繼續(xù)存在,不會(huì)被垃圾回收器回收,其函數(shù)參數(shù)x和局部變量a都會(huì)在這個(gè)調(diào)用對(duì)象中得以維持。雖然調(diào)用對(duì)象不能被直接訪問(wèn),但是該調(diào)用對(duì)象已成為內(nèi)部函數(shù)作用域鏈中的一部分,可以被內(nèi)部函數(shù)訪問(wèn)并修改,所以執(zhí)行test()時(shí),可以正確訪問(wèn)x和a。所以說(shuō), 當(dāng)執(zhí)行了外部函數(shù)時(shí),生成了閉包,被引用的外部函數(shù)的變量將繼續(xù)存在。
二. 閉包的應(yīng)用
  應(yīng)用1:
  這個(gè)是我在用js模擬排序算法過(guò)程遇到的問(wèn)題。我要輸出每一次插入排序后的數(shù)組,如果在循環(huán)中寫成
  setTimeout(function() { $("proc").innerHTML += arr + "<br/>"; }, i * 500);
會(huì)發(fā)現(xiàn)每次輸出的都是最終排好序的數(shù)組,因?yàn)閍rr數(shù)組不會(huì)為你保留每次排序的狀態(tài)值。為了保存會(huì)不斷發(fā)生變化的數(shù)組值,我們用外面包裹一層函數(shù)來(lái)實(shí)現(xiàn)閉包,用閉包存儲(chǔ)這個(gè)動(dòng)態(tài)數(shù)據(jù)。下面用了2種方式實(shí)現(xiàn)閉包,一種是用參數(shù)存儲(chǔ)數(shù)組的值,一種是用臨時(shí)變量存儲(chǔ),后者必須要深拷貝。所有要通過(guò)閉包存儲(chǔ)非持久型變量,均可以用臨時(shí)變量或參數(shù)兩種方式實(shí)現(xiàn)。

[Ctrl+A 全選 注:引入外部Js需再刷新一下頁(yè)面才能執(zhí)行]

應(yīng)用2:
  這個(gè)是無(wú)憂上的例子(點(diǎn)擊這里查看原帖),為每個(gè)<li>結(jié)點(diǎn)綁定click事件彈出循環(huán)的索引值。起初寫成
id.onclick = function(){ alert(i); }  id.onclick = function(){alert(i);}
發(fā)現(xiàn)最終彈出的都是4,而不是想要的 1、2、3,因?yàn)檠h(huán)完畢后i值變成了4。為了保存i的值,同樣我們用閉包實(shí)現(xiàn):

[Ctrl+A 全選 注:引入外部Js需再刷新一下頁(yè)面才能執(zhí)行]

(ps:var a = (function(){})(); 與 var a =new function(){}效果是一樣的,均表示自執(zhí)行函數(shù)。)
  應(yīng)用3:
  下面的code是緩存的應(yīng)用,catchNameArr。在匿名函數(shù)的調(diào)用對(duì)象中保存catch的值,返回的對(duì)象由于被CachedBox變量引用導(dǎo)致匿名函數(shù)的調(diào)用對(duì)象不會(huì)被回收,從而保持了catch的值??梢酝ㄟ^(guò)CachedBox.getCatch("regionId");來(lái)操作,若找不到regionId則從后臺(tái)取,catchNameArr 主要是為了防止緩存過(guò)大。
復(fù)制代碼 代碼如下:

<script type="text/javascript">
var CachedBox = (function() {
var cache = {}, catchNameArr = [], catchMax = 10000;
return {
getCatch: function(name) {
if (name in cache) {
return cache[name];
}
var value = GetDataFromBackend();
cache[name] = value;
catchNameArr.push(name);
this.clearOldCatch();
return value;
},
clearOldCatch: function() {
if (catchNameArr.length > catchMax) {
delete cache[catchNameArr.shift()];
}
}
};
})();
</script>

同理,也可以用這種思想實(shí)現(xiàn)自增長(zhǎng)的ID?! ?
復(fù)制代碼 代碼如下:

<script type="text/javascript">
var GetId = (function() {
var id = 0;
return function() {
return id++;
}
})();
var newId1 = GetId();
var newId2 = GetId();
</script>

應(yīng)用4:
  這個(gè)是無(wú)憂上月MM的例子(點(diǎn)擊這里查看原帖),用閉包實(shí)現(xiàn)程序的暫停執(zhí)行功能,還蠻創(chuàng)意的。

[Ctrl+A 全選 注:引入外部Js需再刷新一下頁(yè)面才能執(zhí)行]

把這個(gè)作用延伸下,我想到了用他來(lái)實(shí)現(xiàn)window.confirm。

[Ctrl+A 全選 注:引入外部Js需再刷新一下頁(yè)面才能執(zhí)行]

看了上面的這些應(yīng)用,再回到前面的一句話:在動(dòng)態(tài)執(zhí)行環(huán)境中,數(shù)據(jù)實(shí)時(shí)地發(fā)生變化,為了保持這些非持久型變量的值,我們用閉包這種載體來(lái)存儲(chǔ)這些動(dòng)態(tài)數(shù)據(jù)。這就是閉包的作用。也就說(shuō)遇到需要存儲(chǔ)動(dòng)態(tài)變化的數(shù)據(jù)或?qū)⒈换厥盏臄?shù)據(jù)時(shí),我們可以通過(guò)外面再包裹一層函數(shù)形成閉包來(lái)解決。
  當(dāng)然,閉包會(huì)導(dǎo)致很多外部函數(shù)的調(diào)用對(duì)象不能釋放,濫用閉包會(huì)使得內(nèi)存泄露,所以在頻繁生成閉包的情景下我們要估計(jì)下他帶來(lái)的副作用。
  畢了。希望能對(duì)大家有所幫助。
者:JayChow
出處:http://ljchow.cnblogs.com

相關(guān)文章

最新評(píng)論