閉包是可以包含自由(未綁定到特定對(duì)象)變量的代碼塊;這些變量不是在這個(gè)代碼塊內(nèi)或者任何全局上下文中定義的,而是在定義代碼塊的環(huán)境中定義(局部變量)。“閉包” 一詞來(lái)源于以下兩者的結(jié)合:要執(zhí)行的代碼塊(由于自由變量被包含在代碼塊中,這些自由變量以及它們引用的對(duì)象沒(méi)有被釋放)和為自由變量提供綁定的計(jì)算環(huán)境(作用域)。在 Scala、Scheme、Common Lisp、Smalltalk、Groovy、JavaScript、Ruby、 Python、Go、Lua、objective c、swift 以及Java(Java8及以上)等語(yǔ)言中都能找到對(duì)閉包不同程度的支持。

成都創(chuàng)新互聯(lián)公司專業(yè)提供中國(guó)電信云錦天府服務(wù),為用戶提供五星數(shù)據(jù)中心、電信、雙線接入解決方案,用戶可自行在線購(gòu)買中國(guó)電信云錦天府服務(wù),并享受7*24小時(shí)金牌售后服務(wù)。
閉包 (closure)是個(gè)精確但又很難解釋的電腦名詞。在 Perl 里面,閉包是以 匿名函數(shù)的形式來(lái)實(shí)現(xiàn),具有持續(xù)參照位于該函數(shù)范圍之外的文字式變數(shù)值的能力。這些外部的文字變數(shù)會(huì)神奇地保留它們?cè)陂]包函數(shù)最初定義時(shí)的值 (深連結(jié))。
如果一個(gè)程式語(yǔ)言容許函數(shù)遞回另一個(gè)函數(shù)的話 (像 Perl 就是),閉包便具有意義。要注意的是,有些語(yǔ)言雖提供匿名函數(shù)的功能,但卻無(wú)法正確處理閉包; Python 這個(gè)語(yǔ)言便是一例。如果要想多了解閉包的話,建議你去找本功能性程式 設(shè)計(jì)的教科書來(lái)看。Scheme這個(gè)語(yǔ)言不僅支持閉包,更鼓勵(lì)多加使用。
以下是個(gè)典型的產(chǎn)生函數(shù)的函數(shù):
sub add_function_generator {
return sub { shift + shift };
}
$add_sub = add_function_generator();
$sum = $add_sub(4,5); # $sum是 9了
閉包用起來(lái)就像是個(gè)函數(shù)樣板,其中保留了一些可以在稍後再填入的空格。add_function_generator() 所遞回的匿名函數(shù)在技術(shù)上來(lái)講并不能算是一個(gè)閉包, 因?yàn)樗鼪](méi)有用到任何位在這個(gè)函數(shù)范圍之外的文字變數(shù)。
把上面這個(gè)例子和下面這個(gè)make_adder()函數(shù)對(duì)照一下,下面這個(gè)函數(shù)所遞回的匿名函數(shù)中使用了一個(gè)外部的文字變數(shù)。這種指明外部函數(shù)的作法需要由 Perl遞回一個(gè)適當(dāng)?shù)拈]包,因此那個(gè)文字變數(shù)在匿名函數(shù)產(chǎn)生之時(shí)的值便永久地被鎖進(jìn)閉包里。
sub make_adder {
my $addpiece = shift;
return sub { shift + $addpiece };
}
$f1 = make_adder(20);
$f2 = make_adder(555);
這樣一來(lái)$f1($n) 永遠(yuǎn)會(huì)是 20加上你傳進(jìn)去的值$n ,而$f2($n) 將 永遠(yuǎn)會(huì)是 555加上你傳進(jìn)去的值$n。$addpiece的值會(huì)在閉包中保留下來(lái)。
閉包在比較實(shí)際的場(chǎng)合中也常用得到,譬如當(dāng)你想把一些程式碼傳入一個(gè)函數(shù)時(shí):
my $line;
timeout(30,sub { $line = STDIN });
如果要執(zhí)行的程式碼當(dāng)初是以字串的形式傳入的話,即'$line = STDIN' ,那么timeout() 這個(gè)假想的函數(shù)在回到該函數(shù)被呼叫時(shí)所在的范圍後便無(wú)法再擷取$line這個(gè)文字變數(shù)的值了。
語(yǔ)法結(jié)構(gòu)編輯
Groovy的閉包
閉包(Closure)是Java所不具備的語(yǔ)法結(jié)構(gòu)。閉包就是一個(gè)代碼塊,用“{ }”包起來(lái)。此時(shí),程序代碼也就成了數(shù)據(jù),可以被一個(gè)變量所引用(與C語(yǔ)言的函數(shù)指針比較類似)。閉包的最典型的應(yīng)用是實(shí)現(xiàn)回調(diào)函數(shù)(callback)。Groovy的API大量使用閉包,以實(shí)現(xiàn)對(duì)外開(kāi)放。閉包的創(chuàng)建過(guò)程很簡(jiǎn)單,例如:
{ 參數(shù) -
代碼...
}
參考下面的例子代碼,定義了c1和c2兩個(gè)閉包,并對(duì)它們進(jìn)行調(diào)用:
def c1 = { println it }
def c2 = { text - println text }
c1.call("content1") //用call方法調(diào)用閉包
c2("content2") //直接調(diào)用閉包
“-;”之前的部分為閉包的參數(shù),如果有多個(gè)參數(shù),之間可用逗號(hào)分割;“-;”之后的部分為閉包內(nèi)的程序代碼。如果省略了“-;”和它之前的部分,此時(shí)閉包中代碼,可以用名為“it”的變量訪問(wèn)參數(shù)。
閉包的返回值和函數(shù)的返回值定義方式是一樣的:如果有return語(yǔ)句,則返回值是return語(yǔ)句后面的內(nèi)容;如果沒(méi)有return語(yǔ)句,則閉包內(nèi)的最后一行代碼就是它的返回值。[1]
環(huán)境表達(dá)編輯
在Javascript中閉包(Closure)
什么是閉包
“官方”的解釋是:所謂“閉包”,指的是一個(gè)擁有許多變量和綁定了這些變量的環(huán)境的表達(dá)式(通常是一個(gè)函數(shù)),因而這些變量也是該表達(dá)式的一部分。
相信很少有人能直接看懂這句話,因?yàn)樗枋龅奶珜W(xué)術(shù)。我想用如何在Javascript中創(chuàng)建一個(gè)閉包來(lái)告訴你什么是閉包,因?yàn)樘^(guò)閉包的創(chuàng)建過(guò)程直接理解閉包的定義是非常困難的。看下面這段
代碼
1
2
3
4
5
6
7
8
9
function a(){
var i=0;
function b(){
alert(++i);
}
return b;
}
var c=a();
c();
特點(diǎn)
這段代碼有兩個(gè)特點(diǎn):
1、函數(shù)b嵌套在函數(shù)a內(nèi)部;
2、函數(shù)a返回函數(shù)b。
這樣在執(zhí)行完var c=a( )后,變量c實(shí)際上是指向了函數(shù)b,再執(zhí)行c( )后就會(huì)彈出一個(gè)窗口顯示i的值(第一次為1)。這段代碼其實(shí)就創(chuàng)建了一個(gè)閉包,為什么?因?yàn)楹瘮?shù)a外的變量c引用了函數(shù)a內(nèi)的函數(shù)b,就是說(shuō):
當(dāng)函數(shù)a的內(nèi)部函數(shù)b被函數(shù)a外的一個(gè)變量引用的時(shí)候,就創(chuàng)建了一個(gè)閉包。
作用
簡(jiǎn)而言之,閉包的作用就是在a執(zhí)行完并返回后,閉包使得Javascript的垃圾回收機(jī)制GC不會(huì)收回a所占用的資源,因?yàn)閍的內(nèi)部函數(shù)b的執(zhí)行需要依賴a中的變量。這是對(duì)閉包作用的非常直白的描述,不專業(yè)也不嚴(yán)謹(jǐn),但大概意思就是這樣,理解閉包需要循序漸進(jìn)的過(guò)程。
在上面的例子中,由于閉包的存在使得函數(shù)a返回后,a中的i始終存在,這樣每次執(zhí)行c(),i都是自加1后alert出i的值。
那 么我們來(lái)想象另一種情況,如果a返回的不是函數(shù)b,情況就完全不同了。因?yàn)閍執(zhí)行完后,b沒(méi)有被返回給a的外界,只是被a所引用,而此時(shí)a也只會(huì)被b引 用,因此函數(shù)a和b互相引用但又不被外界打擾(被外界引用),函數(shù)a和b就會(huì)被GC回收。(關(guān)于Javascript的垃圾回收機(jī)制將在后面詳細(xì)介紹)
另一個(gè)例子
模擬私有變量
function Counter(start){
var count = start;
return{
increment:function(){
count++;
},
get:function(){
return count;
}
}
}
var foo =Counter(4);
foo.increment();
foo.get();// 5
結(jié)果
這里,Counter 函數(shù)返回兩個(gè)閉包,函數(shù) increment 和函數(shù) get。 這兩個(gè)函數(shù)都維持著 對(duì)外部作用域 Counter 的引用,因此總可以訪問(wèn)此作用域內(nèi)定義的變量 count.
文法
objective c的閉包(block)
objective c 中的的閉包,是通過(guò)block實(shí)現(xiàn)的。Apple在C,Objective-C和C++中擴(kuò)充了Block這種文法的,并且在GCC4.2中進(jìn)行了支持。你可以把它理解為函數(shù)指針,匿名函數(shù),閉包,lambda表達(dá)式,這里暫且用塊對(duì)象來(lái)表述,因?yàn)樗鼈冎g還是有些許不同的。
聲明一個(gè)塊
如果以內(nèi)聯(lián)方式使用塊對(duì)象,則無(wú)需聲明。塊對(duì)象聲明語(yǔ)法與函數(shù)指針聲明語(yǔ)法相似,但是塊對(duì)象應(yīng)使用脫字符(^)而非星號(hào)指針 (*)。下面的代碼聲明一個(gè)aBlock變量,它標(biāo)識(shí)一個(gè)需傳入三個(gè)參數(shù)并具有float返回值的塊。
float (^aBlock)(const int*, int, float);
l 創(chuàng)建一個(gè)塊
塊使用脫字符(^)作為起始標(biāo)志,使用分號(hào)作為結(jié)束標(biāo)志。下面的例子聲明一個(gè)簡(jiǎn)單塊,并且將其賦給之前聲明的block變量(oneFrom)。
int (^oneFrom)(int);
oneFrom = ^(int anInt) {
return anInt - 1;
};
微觀世界
如 果要更加深入的了解閉包以及函數(shù)a和嵌套函數(shù)b的關(guān)系,我們需要引入另外幾個(gè)概念:函數(shù)的執(zhí)行環(huán)境(execution context)、活動(dòng)對(duì)象(call object)、作用域(scope)、作用域鏈(scope chain)。以函數(shù)a從定義到執(zhí)行的過(guò)程為例闡述這幾個(gè)概念。
1、當(dāng)定義函數(shù)a的時(shí)候,js解釋器會(huì)將函數(shù)a的作用域鏈(scope chain)設(shè)置為定義a時(shí)a所在的“環(huán)境”,如果a是一個(gè)全局函數(shù),則scope chain中只有window對(duì)象。
2、當(dāng)函數(shù)a執(zhí)行的時(shí)候,a會(huì)進(jìn)入相應(yīng)的執(zhí)行環(huán)境(execution context)。
3、在創(chuàng)建執(zhí)行環(huán)境的過(guò)程中,首先會(huì)為a添加一個(gè)scope屬性,即a的作用域,其值就為第1步中的scope chain。即a.scope=a的作用域鏈。
4、然后執(zhí)行環(huán)境會(huì)創(chuàng)建一個(gè)活動(dòng)對(duì)象(call object)。活動(dòng)對(duì)象也是一個(gè)擁有屬性的對(duì)象,但它不具有原型而且不能通過(guò)JavaScript代碼直接訪問(wèn)。創(chuàng)建完活動(dòng)對(duì)象后,把活動(dòng)對(duì)象添加到a的作用域鏈的最頂端。此時(shí)a的作用域鏈包含了兩個(gè)對(duì)象:a的活動(dòng)對(duì)象和window對(duì)象。
5、下一步是在活動(dòng)對(duì)象上添加一個(gè)arguments屬性,它保存著調(diào)用函數(shù)a時(shí)所傳遞的參數(shù)。
6、最后把所有函數(shù)a的形參和內(nèi)部的函數(shù)b的引用也添加到a的活動(dòng)對(duì)象上。在這一步中,完成了函數(shù)b的的定義,因此如同第3步,函數(shù)b的作用域鏈被設(shè)置為b所被定義的環(huán)境,即a的作用域。
到此,整個(gè)函數(shù)a從定義到執(zhí)行的步驟就完成了。此時(shí)a返回函數(shù)b的引用給c,又函數(shù)b的作用域鏈包含了對(duì)函數(shù)a的活動(dòng)對(duì)象的引用,也就是說(shuō)b可以訪問(wèn)到a中定義的所有變量和函數(shù)。函數(shù)b被c引用,函數(shù)b又依賴函數(shù)a,因此函數(shù)a在返回后不會(huì)被GC回收。
當(dāng)函數(shù)b執(zhí)行的時(shí)候亦會(huì)像以上步驟一樣。因此,執(zhí)行時(shí)b的作用域鏈包含了3個(gè)對(duì)象:b的活動(dòng)對(duì)象、a的活動(dòng)對(duì)象和window對(duì)象,如下圖所示:
如圖所示,當(dāng)在函數(shù)b中訪問(wèn)一個(gè)變量的時(shí)候,搜索順序是先搜索自身的活動(dòng)對(duì)象,如果存在則返回,如果不存在將繼續(xù)搜索函數(shù)a的活動(dòng)對(duì)象,依 次查找,直到找到為止。如果整個(gè)作用域鏈上都無(wú)法找到,則返回undefined。如果函數(shù)b存在prototype原型對(duì)象,則在查找完自身的活動(dòng)對(duì)象 后先查找自身的原型對(duì)象,再繼續(xù)查找。這就是Javascript中的變量查找機(jī)制。
應(yīng)用場(chǎng)景
1、保護(hù)函數(shù)內(nèi)的變量安全。以最開(kāi)始的例子為例,函數(shù)a中i只有函數(shù)b才能訪問(wèn),而無(wú)法通過(guò)其他途徑訪問(wèn)到,因此保護(hù)了i的安全性。
2、在內(nèi)存中維持一個(gè)變量。依然如前例,由于閉包,函數(shù)a中i的一直存在于內(nèi)存中,因此每次執(zhí)行c(),都會(huì)給i自加1。
以上兩點(diǎn)是閉包最基本的應(yīng)用場(chǎng)景,很多經(jīng)典案例都源于此。
回收機(jī)制
在Javascript中,如果一個(gè)對(duì)象不再被引用,那么這個(gè)對(duì)象就會(huì)被GC回收。如果兩個(gè)對(duì)象互相引用,而不再被第3者所引用,那么這兩個(gè)互相引用的對(duì)象也會(huì)被回收。因?yàn)楹瘮?shù)a被b引用,b又被a外的c引用,這就是為什么函數(shù)a執(zhí)行后不會(huì)被回收的原因。
匿名內(nèi)部
在Python中的閉包(Closure)
學(xué)過(guò)Java GUI編程的人都知道定義匿名內(nèi)部類是注冊(cè)監(jiān)聽(tīng)等處理的簡(jiǎn)潔有效手段,閉包的定義方式有點(diǎn)類似于這種匿名內(nèi)部類,
但是閉包的作用威力遠(yuǎn)遠(yuǎn)超過(guò)匿名內(nèi)部類,這也是很多流行動(dòng)態(tài)語(yǔ)言選擇閉包的原因,相信你在JavaScript中已經(jīng)了解它的神奇功效了。
定義
如果在一個(gè)內(nèi)部函數(shù)里,對(duì)在外部作用域(但不是在全局作用域)的變量進(jìn)行引用,那么內(nèi)部函數(shù)就被認(rèn)為是閉包(closure)。
簡(jiǎn)單閉包的例子:
下面是一個(gè)使用閉包簡(jiǎn)單的例子,模擬一個(gè)計(jì)數(shù)器,通過(guò)將整型包裹為一個(gè)列表的單一元素來(lái)模擬使看起來(lái)更易變:
函數(shù)counter()所作的唯一一件事就是接受一個(gè)初始化的值來(lái)計(jì)數(shù),并將該值賦給列表count成員,然后定義一個(gè)內(nèi)部函數(shù)incr()。通過(guò)內(nèi)部函數(shù)使用變量count,就創(chuàng)建了一個(gè)閉包。最魔法的地方是counter()函數(shù)返回一個(gè)incr(),一個(gè)可以調(diào)用的函數(shù)對(duì)象。
運(yùn)行:
c = counter⑸
type(c)
type 'function'
print c()6
print c()
7
代碼格式較重要
c2 = counter(99)
100
print c()
8
閉包是指有權(quán)訪問(wèn)另外一個(gè)函數(shù)作用域中的變量的函數(shù)。
閉包就是函數(shù)的局部變量集合,只是這些局部變量在函數(shù)返回后會(huì)繼續(xù)存在。閉包就是就是函數(shù)的“堆棧”在函數(shù)返回后并不釋放,我們也可以理解為這些函數(shù)堆棧并不在棧上分配而是在堆上分配。當(dāng)在一個(gè)函數(shù)內(nèi)定義另外一個(gè)函數(shù)就會(huì)產(chǎn)生閉包。
作用是:匿名自執(zhí)行函數(shù):我們知道所有的變量,如果不加上var關(guān)鍵字,則默認(rèn)的會(huì)添加到全局對(duì)象的屬性上去,這樣的臨時(shí)變量加入全局對(duì)象有很多壞處,如:別的函數(shù)可能誤用這些變量;造成全局對(duì)象過(guò)于龐大,影響訪問(wèn)速度(因?yàn)樽兞康娜≈凳切枰獜脑玩溕媳闅v的)。除了每次使用變量都是用var關(guān)鍵字外,我們?cè)趯?shí)際情況下經(jīng)常遇到這樣一種情況,即有的函數(shù)只需要執(zhí)行一次,其內(nèi)部變量無(wú)需維護(hù),可以用閉包。
結(jié)果緩存:我們開(kāi)發(fā)中會(huì)碰到很多情況,設(shè)想我們有一個(gè)處理過(guò)程很耗時(shí)的函數(shù)對(duì)象,每次調(diào)用都會(huì)花費(fèi)很長(zhǎng)時(shí)間,那么我們就需要將計(jì)算出來(lái)的值存儲(chǔ)起來(lái),當(dāng)調(diào)用這個(gè)函數(shù)的時(shí)候,首先在緩存中查找,如果找不到,則進(jìn)行計(jì)算,然后更新緩存并返回值,如果找到了,直接返回查找到的值即可。閉包正是可以做到這一點(diǎn),因?yàn)樗粫?huì)釋放外部的引用,從而函數(shù)內(nèi)部的值可以得以保留。
閉包的理解:閉包就是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)。
閉包是一種保護(hù)私有變量的機(jī)制,函數(shù)執(zhí)行時(shí)形成私有作用域,保護(hù)私有變量不受外界影響。內(nèi)部函數(shù)可以使用外部函數(shù)的參數(shù)和變量,函數(shù)和變量不會(huì)被垃圾回收機(jī)制回收。
閉包的價(jià)值在于可以作為函數(shù)對(duì)象 或者匿名函數(shù),對(duì)于類型系統(tǒng)而言這就意味著不僅要表示數(shù)據(jù)還要表示代碼。支持閉包的多數(shù)語(yǔ)言都將函數(shù)作為第一級(jí)對(duì)象,就是說(shuō)這些函數(shù)可以存儲(chǔ)到變量中、作為參數(shù)傳遞給其他函數(shù),最重要的是能夠被函數(shù)動(dòng)態(tài)地創(chuàng)建和返回。
作用是:匿名自執(zhí)行函數(shù):我們知道所有的變量,如果不加上var關(guān)鍵字,則默認(rèn)的會(huì)添加到全局對(duì)象的屬性上去,這樣的臨時(shí)變量加入全局對(duì)象有很多壞處,如:別的函數(shù)可能誤用這些變量;造成全局對(duì)象過(guò)于龐大,影響訪問(wèn)速度(因?yàn)樽兞康娜≈凳切枰獜脑玩溕媳闅v的)。
閉包對(duì)頁(yè)面的影響:
1、防止全局污染。
2、內(nèi)部的函數(shù)可以用外部的變量或參數(shù)。
3、內(nèi)部的變量不會(huì)被垃圾回收機(jī)制回收。
4、閉包可以更新外部的變量的值。
5、用閉包可以模擬私有方法。
6、由于閉包會(huì)使得函數(shù)中的變量都被保存在內(nèi)存中,內(nèi)存消耗很大,所以不能濫用閉包,否則會(huì)造成頁(yè)面性能的問(wèn)題;閉包和全局變量一樣,如果大量使用可能會(huì)造成內(nèi)存泄漏。
本文名稱:go語(yǔ)言閉包的作用 go語(yǔ)言封裝
文章地址:http://chinadenli.net/article24/ddgspce.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供用戶體驗(yàn)、網(wǎng)站改版、網(wǎng)站制作、網(wǎng)站建設(shè)、電子商務(wù)、建站公司
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)