官方對(duì)閉包的解釋是:一個(gè)擁有許多變量和綁定了這些變量的環(huán)境的表達(dá)式(通常是一個(gè)函數(shù)),因而這些變量也是該表達(dá)式的一部分。
一、javascript閉包的特點(diǎn):
1.作為一個(gè)函數(shù)變量的一個(gè)引用,當(dāng)函數(shù)返回時(shí),其處于激活狀態(tài)。
2.一個(gè)閉包就是當(dāng)一個(gè)函數(shù)返回時(shí),一個(gè)沒(méi)有釋放資源的棧區(qū)。
簡(jiǎn)單的說(shuō),javascript允許使用內(nèi)部函數(shù)---即函數(shù)定義和函數(shù)表達(dá)式位于另一個(gè)函數(shù)的函數(shù)體內(nèi)。而且,這些內(nèi)部函數(shù)可以訪問(wèn)它們所在的外部函數(shù)中聲明的所有局部變量、參數(shù)和聲明的其他內(nèi)部函數(shù)。當(dāng)其中一個(gè)這樣的內(nèi)部函數(shù)在包含它們的外部函數(shù)之外被調(diào)用時(shí),就會(huì)形成閉包。
二、javascript閉包的原理
閉包是指有權(quán)訪問(wèn)另一個(gè)函數(shù)作用域中的變量的函數(shù)。根據(jù)下面的代碼示例來(lái)理解什么是閉包,在add函數(shù)內(nèi)部的匿名函數(shù)中,訪問(wèn)到外部函數(shù)的變量outerArg,在執(zhí)行add(10)之后外部函數(shù)返回了,并且將內(nèi)部的匿名函數(shù)賦值給了變量addTen,此時(shí)通過(guò)addTen調(diào)用函數(shù),依然可以訪問(wèn)到outerArg,也就是10。這個(gè)閉包中的變量,只能通過(guò)調(diào)用addTen函數(shù)訪問(wèn),無(wú)法通過(guò)其他渠道訪問(wèn)到,下面代碼最后一行通過(guò)輸出屬性的方式嘗試訪問(wèn)結(jié)果是輸出undefined。
outerArg是屬于add函數(shù)的作用域中的變量,addTen有權(quán)訪問(wèn)add函數(shù)作用域中的變量,因此addTen是一個(gè)閉包。閉包產(chǎn)生的本質(zhì)是:在一個(gè)函數(shù)(外部函數(shù))內(nèi)部定義的函數(shù)(內(nèi)部函數(shù))會(huì)將外部函數(shù)作用域中的活動(dòng)對(duì)象添加到自己的作用域鏈中,下面代碼中inner函數(shù)將add函數(shù)的outerArg添加到自己的作用域鏈上。在add函數(shù)執(zhí)行完之后,其執(zhí)行環(huán)境會(huì)被銷毀,但由于inner函數(shù)還在引用outerArg,所以outerArg不會(huì)被銷毀,依然保留在inner函數(shù)的作用域鏈中。直到inner函數(shù)(addTen函數(shù))被銷毀之后,outerArg才會(huì)跟著其作用域鏈一起被銷毀。由于閉包變量是位于作用域鏈上,因此必須調(diào)用閉包函數(shù)進(jìn)入其作用域之后才能訪問(wèn)到閉包變量。
function add(outerArg) {
function inner(innerArg) {
return innerArg + outerArg;
}
return inner;
}
var addTen = add(10);
console.log(addTen(1)); // 輸出11
console.log(addTen(2)); // 輸出12
console.log(addTen(3)); // 輸出13
console.log(addTen.outerArg); // undefined123456789101112
三、javascript閉包的應(yīng)用
javascript閉包的應(yīng)用1:
這個(gè)是我在用js模擬排序算法過(guò)程遇到的問(wèn)題。我要輸出每一次插入排序后的數(shù)組,如果在循環(huán)中寫(xiě)成
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)。
《!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”》
《html xmlns=“http://www.w3.org/1999/xhtml”》
《head》
《title》《/title》
《script type=“text/javascript”》《!--
var arr = [4, 5, 6, 8, 7, 9, 3, 2, 1, 0];
var $ = function(id) { return document.getElementById(id); }
var Sort = {
Insert: function() {
for (var i = 1; i 《 arr.length; i++) {
for (var j = 0; j 《 i; j++) {
if (arr[i] 《 arr[j]) {
arr[i] = [arr[j], arr[j] = arr[i]][0];
}
}
setTimeout((function() {
var m = [];
for (var j = 0; j 《 arr.length; j++) {
m[j] = arr[j];
}
return function() {
$(“proc”).innerHTML += m + “《br /》”;
}
})(), i * 500);
//or 寫(xiě)成下面這樣也可以
/*
setTimeout((function(m) {
return function() {
$(“proc”).innerHTML += m + “《br /》”;
}
})(arr.join(“,”)), i * 500);
*/
}
return arr;
}
}
// --》《/script》
《/head》
《body》
《div》
var a = [4, 5, 6, 8, 7, 9, 3, 2, 1, 0];《/div》
《div》
《input type=“button” value=“插入排序” onclick=“Sort.Insert();” /》
《/div》
Proc:《br /》
《div id=“proc”》
《/div》
《/body》
《/html》
javascript閉包的應(yīng)用2:
這個(gè)是無(wú)憂上的例子,為每個(gè)《li》結(jié)點(diǎn)綁定click事件彈出循環(huán)的索引值。起初寫(xiě)成
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):
《!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”》
《html xmlns=“http://www.w3.org/1999/xhtml”》
《head》
《title》《/title》
《script type=“text/javascript”》《!--
window.onload = function() {
for (var i = 1; i 《 4; i++) {
var id = document.getElementById(“a” + i);
id.onclick = (function(i) {
return function() {
alert(i);
}
})(i);
}
}
// --》《/script》
《/head》
《body》
《ul》
《li id=“a1”》aa《/li》
《li id=“a2”》aa《/li》
《li id=“a3”》aa《/li》
《/ul》
《/body》
《/html》
?。╬s:var a = (function(){})(); 與 var a =new function(){}效果是一樣的,均表示自執(zhí)行函數(shù)。)
javascript閉包的應(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ò)大。
《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。
《script type=“text/javascript”》
var GetId = (function() {
var id = 0;
return function() {
return id++;
}
})();
var newId1 = GetId();
var newId2 = GetId();
《/script》
評(píng)論
查看更多