閉包是由函式及其相關引用環境組合而成的實體(即:閉包=函式+引用環境)。
Go中的閉包
func f() func() int {
i:=0
return func() int {
i++
return i
}
}
複製程式碼
函式f返回了一個函式,返回的這個函式,返回的這個函式就是一個閉包。這個函式本身沒有定義變數的,而是引用了它所在的環境(函式f)中的變數i。
c1 := f()
c2 := f()
fmt.Println(c1()) // output: 1
fmt.Println(c2()) // output: 1
fmt.Println(c2()) // output: 2
fmt.Println(c1()) // output: 2
複製程式碼
函式f每呼叫一次,就形成了一個新的環境,對應的閉包中,函式都是同一個函式,環境卻是引用不同的環境。
閉包環境中引用的變數是不能夠在棧上分配的,而是在堆上分配。因為如果引用的變數在棧上分配,那麼該變數會跟隨函式f返回之後回收,那麼閉包函式就不可能訪問未分配的一個變數,即未宣告的變數,之所以能夠再堆上分配,而不是在棧上分配,是Go的一個語言特性----escape analyze(能夠自動分析出變數的作用範圍,是否將變數分配堆上)。
閉包的底層實現
Go在底層使用類似結構體的形式表示一個閉包。
我們可以把上面的閉包表示成這樣,即:
type Closure struct{
func()() //匿名函式地址,當然語法要求一定要有變數名,這裡只是為了表達匿名的含義
i *int //引用的變數地址
}
複製程式碼
小結
-
Go語言支援閉包
-
Go語言能通過escape analyze識別出變數的作用域,自動將變數在堆上分配。將閉包環境變數在堆上分配是Go實現閉包的基礎。
-
返回閉包時並不是單純返回一個函式,而是返回了一個結構體,記錄下函式返回地址和引用的環境中的變數地址。