匿名函數就是沒有定義函數名稱的函數。我們可以在函數內部定義匿名函數,也叫函數嵌套。
匿名函數可以直接被調用,也可以賦值給變量、作爲參數或返回值。比如:
func main(){ func(s string){ //直接被調用 println(s) }("hello gopher!!!") /* func(s string){ //未被調用 println(s) } */ } func main(){ hi := func(s string){ //賦值給變量 println(s) } hi("hello gopher!!!") } func test(f func(string)){ f("hello gopher!!!") } func main(){ hi := func(s string){ println(s) } test(hi) //作爲參數 } func test()func(string){ hi := func(s string){ //作爲返回值 println(s) } return hi } func main(){ f := test() f("hello gopher!!!") }
普通函數和匿名函數都可以作爲結構體的字段,比如:
{ type calc struct{ mul func(x,y int)int } x := calc{ mul: func(x,y int)int{ return (x*y) }, } println(x.mul(2,3)) }
也可以經channel(通道)傳遞,比如:
{ c := make(chan func(int, int)int, 2) c <- func(x,y int) int {return x + y} println((<-c)(2,3)) }
閉包(closure)
閉包是指在上下文中引用了自由變量(未綁定到特定對象)的代碼塊(函數),或者說是代碼塊(函數)與和引用環境的組合體。比如:
func intSeq()func()int{ i := 0 println(&i) return func()int{ i += 1 println(&i,i) return i } } func main(){ nextInt := intSeq() fmt.Println(nextInt()) fmt.Println(nextInt()) fmt.Println(nextInt()) newInt := intSeq() fmt.Println(newInt()) } 輸出: 0xc42000a320 0xc42000a320 1 1 0xc42000a320 2 2 0xc42000a320 3 3 0xc42007a010 0xc42007a010 1 1
當nextInt函數返回後,通過輸出指針,我們可以看出函數在main運行時,依然引用的是原環境變量指針,這種現象稱作閉包。所以說,閉包是函數和引用環境變量的組合體。
因爲閉包是通過指針引用環境變量,那麼就會導致該變量的生命週期
變長,甚至被分配到堆內存。如果多個匿名函數引用同一個環境變量,會讓事情變得更加複雜,比如:
func test()[]func(){ var s []func() for i:= 0;i < 3;i++{ s = append(s, func(){ println(&i , i) }) } return s } func main(){ funcSlice := test() for _ , f := range funcSlice{ f() } } 輸出: 0xc42000a320 3 0xc42000a320 3 0xc42000a320 3
解決方法就是每次用不同的環境變量或參數賦值,比如修改後的test函數:
func test()[]func(){ var s []func() for i:= 0;i < 3;i++{ x := i s = append(s, func(){ println(&x , x) }) } return s }
閉包在不用傳遞參數的情況下就可以讀取和修改環境變量,當然我們是要爲這種遍歷付出代價的,所以日常開發中,在高併發服務
的場景下建議慎用,除非你明確你的需求必須這樣做。