标签:golang 匿名函数 闭包
匿名函数就是没有定义函数名称的函数。我们可以在函数内部定义匿名函数,也叫函数嵌套。
匿名函数可以直接被调用,也可以赋值给变量、作为参数或返回值。比如:
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 }
闭包在不用传递参数的情况下就可以读取和修改环境变量,当然我们是要为这种遍历付出代价的,所以日常开发中,在高并发服务
的场景下建议慎用,除非你明确你的需求必须这样做。
本文出自 “博学于文,约之于礼” 博客,转载请与作者联系!
标签:golang 匿名函数 闭包
原文地址:http://guanyu.blog.51cto.com/8372396/1895722