函數(shù)
在go中,既可以在包中定義函數(shù)刊懈,也可以定義結構體的函數(shù)这弧,對于后者,函數(shù)接收者(結構體對象)是它的第一個參數(shù)虚汛。他們都會在運行時被構造成funcval
對象匾浪,而閉包就是含有捕獲變量的funcval
對象
如結構體
type A struct {
Name string
}
func (a A) GetName() string {
return a.Name
}
func (a *A) SetName() {
a.Name = "Hello " + a.Name
}
這樣來訪問GetName
方法
func TestFunc1(t *testing.T) {
f := A.GetName
a := A{Name: "a"}
f(a)
}
在內存中的結構
內存中的結構
另一種調用方式
func TestFunc1(t *testing.T) {
a := A{Name: "a"}
fmt.Println(a.GetName())
}
在編譯時,會被編譯成A.GetName(a)
卷哩;在編碼過程中蛋辈,使用對象調用指針函數(shù),指針調用對象函數(shù)将谊,仍然會正常運行冷溶,其實是go提供的語法糖,在編譯時會矯正
func TestFunc2(t *testing.T) {
a := A{Name: "a"}
a.SetName() // 編譯為(&a).SetName()尊浓,運行時執(zhí)行*A.SetName(&a)
fmt.Println(a.Name) // 輸出 Hello a
pa := &a
fmt.Println(pa.GetName()) // 輸出 Hello a逞频,編譯為(*pa).GetName(),運行時執(zhí)行A.GetName(*pa)
}
閉包有3個特點
- 變量在函數(shù)外部定義栋齿,在函數(shù)內部被引用苗胀,該變量為
捕獲變量
- 脫離了形成閉包的上下文襟诸,閉包函數(shù)也能使用
捕獲變量
- 閉包要求捕獲變量在函數(shù)內部與外層函數(shù)表現(xiàn)一致,即捕獲變量會在堆上分配基协,棧中只是存儲了地址歌亲,這樣,外層函數(shù)操作的變量與捕獲的變量是同一個
內部運行原理
- 閉包函數(shù)的指令在編譯時生成
- 由于閉包對象需要保存捕獲變量澜驮,需要在運行時創(chuàng)建閉包對象funcVal
- 在運行時捕獲變量在堆上分配陷揪,棧中只存儲了變量地址,這樣杂穷,外層函數(shù)操作的變量與捕獲的變量時同一個
例子
func create() (fs [2]func()) {
for i:=0; i<2; i++ {
fs[i] = func() {
fmt.Println(i)
}
}
return
}
func TestClosure(t *testing.T) {
fs := create()
for i:=0; i<len(fs);i++ {
fs[i]() // 都會輸出2
}
}
image.png
所以調用
create
方法后悍缠,實際i的值為2了,所以調用fs[i]
時亭畜,打印的都是2