我們先來(lái)看一個(gè)簡(jiǎn)單的閉包函數(shù):
function a(){ //外層函數(shù)
var n = 0; //私有變量
function b(m){ //內(nèi)部函數(shù),私有函數(shù)
n = n + m; //遞加上級(jí)私有變量的值
return n;
}
return b;
}
var c = a();//調(diào)用外層函數(shù),返回內(nèi)部函數(shù)
c();
document.write(b(3));//輸出3
document.write(b(3));//輸出6,3+3
document.write(b(3));//輸出9,6+3
document.write(b(3));//輸出12,9+3
假如不使用如上的閉包函數(shù),當(dāng)調(diào)用外部函數(shù)a之后,其定義的私有變量就不存在,也就無(wú)法實(shí)現(xiàn)值的遞增效果。
所以,我們?nèi)绾螌?duì)外部函數(shù)b的私有變量n的值進(jìn)行長(zhǎng)時(shí)間的保存呢?
使用如上代碼所示的閉包。
如上代碼的步驟是:
(1)定義普通函數(shù) a
(2)在 a 中定義普通函數(shù) b
(3)在 a 中返回 b
(4)執(zhí)行 a, 并把 A 的返回結(jié)果賦值給變量 b
(5)執(zhí)行 c
函數(shù)a的內(nèi)部函數(shù)b被函數(shù)a外的一個(gè)變量 c 引用。即:
當(dāng)一個(gè)內(nèi)部函數(shù)被其外部函數(shù)之外的變量引用時(shí),就形成了一個(gè)閉包。
在了解閉包的作用之前,我們先了解一下 Javascript 中的GC機(jī)制:
在 Javascript 中,如果一個(gè)對(duì)象不再被引用,那么這個(gè)對(duì)象就會(huì)被 GC 回收,否則這個(gè)對(duì)象一直會(huì)保存在內(nèi)存中。
在上述例子中,b 定義在 a 中,因此 b 依賴(lài)于a,而外部變量 c 又引用了b, 所以a間接的被c 引用。
也就是說(shuō),A 不會(huì)被 GC 回收,會(huì)一直保存在內(nèi)存中。上述代碼3,6,9的值一直保存在內(nèi)存中,所以可以實(shí)現(xiàn)疊加。
閉包的高級(jí)寫(xiě)法
(function(document){
var viewport;
var obj = {
init:function(id){
viewport = document.querySelector("#"+id);
},
addChild:function(child){
viewport.appendChild(child);
},
removeChild:function(child){
viewport.removeChild(child);
}
}
window.jView = obj;
})(document);
這個(gè)組件的作用是:初始化一個(gè)容器,然后可以給這個(gè)容器添加子容器,也可以移除一個(gè)容器。
功能很簡(jiǎn)單,但這里涉及到了另外一個(gè)概念:立即執(zhí)行函數(shù)。 簡(jiǎn)單了解一下就行,需要重點(diǎn)理解的是這種寫(xiě)法是如何實(shí)現(xiàn)閉包功能的。
可以將上面的代碼拆分成兩部分:(function(){}) 和 () , 第1個(gè)() 是一個(gè)表達(dá)式,而這個(gè)表達(dá)式本身是一個(gè)匿名函數(shù),所以在這個(gè)表達(dá)式后面加 () 就表示執(zhí)行這個(gè)匿名函數(shù)。
因此這段代碼執(zhí)行執(zhí)行過(guò)程可以分解如下:
var f = function(document){
var viewport;
var obj = {
init:function(id){
viewport = document.querySelector("#"+id);
},
addChild:function(child){
viewport.appendChild(child);
},
removeChild:function(child){
viewport.removeChild(child);
}
}
window.jView = obj;
};
f(document);
在這段代碼中似乎看到了閉包的影子,但 f 中沒(méi)有任何返回值,似乎不具備閉包的條件,注意這句代碼:
window.jView = obj;
obj 是在函數(shù) f 中定義的一個(gè)對(duì)象,這個(gè)對(duì)象中定義了一系列方法, 執(zhí)行window.jView = obj 就是在 window 全局對(duì)象定義了一個(gè)變量 jView,并將這個(gè)變量指向 obj 對(duì)象,即全局變量 jView 引用了 obj . 而 obj 對(duì)象中的函數(shù)又引用了函數(shù) f 中的變量 viewport ,因此函數(shù) f 中的 viewport 不會(huì)被 GC 回收,viewport 會(huì)一直保存到內(nèi)存中,所以這種寫(xiě)法滿(mǎn)足了閉包的條件。