Golang 閉包的實(shí)現(xiàn)

什么是閉包?什么場(chǎng)景下會(huì)用閉包?本文對(duì)?go 語(yǔ)言中的閉包做了詳細(xì)介紹。
閉包是由函數(shù)及其相關(guān)引用環(huán)境組合而成的實(shí)體(即:閉包=函數(shù)+引用環(huán)境)。
Go中的閉包
閉包是函數(shù)式語(yǔ)言中的概念,沒(méi)有研究過(guò)函數(shù)式語(yǔ)言的用戶可能很難理解閉包的強(qiáng)大,相關(guān)的概念超出了本書(shū)的范圍。Go語(yǔ)言是支持閉包的,這里只是簡(jiǎn)單地講一下在Go語(yǔ)言中閉包是如何實(shí)現(xiàn)的。

函數(shù)f返回了一個(gè)函數(shù),返回的這個(gè)函數(shù),返回的這個(gè)函數(shù)就是一個(gè)閉包。這個(gè)函數(shù)中本身是沒(méi)有定義變量i的,而是引用了它所在的環(huán)境(函數(shù)f)中的變量i。

c1跟c2引用的是不同的環(huán)境,在調(diào)用i++時(shí)修改的不是同一個(gè)i,因此兩次的輸出都是1。函數(shù)f每進(jìn)入一次,就形成了一個(gè)新的環(huán)境,對(duì)應(yīng)的閉包中,函數(shù)都是同一個(gè)函數(shù),環(huán)境卻是引用不同的環(huán)境。
變量i是函數(shù)f中的局部變量,假設(shè)這個(gè)變量是在函數(shù)f的棧中分配的,是不可以的。因?yàn)楹瘮?shù)f返回以后,對(duì)應(yīng)的棧就失效了,f返回的那個(gè)函數(shù)中變量i就引用一個(gè)失效的位置了。所以閉包的環(huán)境中引用的變量不能夠在棧上分配。
escape analyze
在繼續(xù)研究閉包的實(shí)現(xiàn)之前,先看一看Go的一個(gè)語(yǔ)言特性:

Cursor是一個(gè)結(jié)構(gòu)體,這種寫(xiě)法在C語(yǔ)言中是不允許的,因?yàn)樽兞縞是在棧上分配的,當(dāng)函數(shù)f返回后c的空間就失效了。但是,在Go語(yǔ)言規(guī)范中有說(shuō)明,這種寫(xiě)法在Go語(yǔ)言中合法的。語(yǔ)言會(huì)自動(dòng)地識(shí)別出這種情況并在堆上分配c的內(nèi)存,而不是函數(shù)f的棧上。
為了驗(yàn)證這一點(diǎn),可以觀察函數(shù)f生成的匯編代碼:

識(shí)別出變量需要在堆上分配,是由編譯器的一種叫escape analyze的技術(shù)實(shí)現(xiàn)的。如果輸入命令:

可以看到輸出:

表示c逃逸了,被移到堆中。escape analyze可以分析出變量的作用范圍,這是對(duì)垃圾回收很重要的一項(xiàng)技術(shù)。
閉包結(jié)構(gòu)體
回到閉包的實(shí)現(xiàn)來(lái),前面說(shuō)過(guò),閉包是函數(shù)和它所引用的環(huán)境。那么是不是可以表示為一個(gè)結(jié)構(gòu)體呢:

事實(shí)上,Go在底層確實(shí)就是這樣表示一個(gè)閉包的。讓我們看一下匯編代碼:

其中func·001是另一個(gè)函數(shù)的函數(shù)地址,也就是f返回的那個(gè)函數(shù)。
小結(jié)
-
Go語(yǔ)言支持閉包 -
Go語(yǔ)言能通過(guò)escape analyze識(shí)別出變量的作用域,自動(dòng)將變量在堆上分配。將閉包環(huán)境變量在堆上分配是Go實(shí)現(xiàn)閉包的基礎(chǔ)。 -
返回閉包時(shí)并不是單純返回一個(gè)函數(shù),而是返回了一個(gè)結(jié)構(gòu)體,記錄下函數(shù)返回地址和引用的環(huán)境中的變量地址。
轉(zhuǎn)自:tiancaiamao.gitbooks.io/go-internals/content/zh/03.6.html
文章轉(zhuǎn)載:Go開(kāi)發(fā)大全(版權(quán)歸原作者所有,侵刪)