golang 函数二 (匿名函数和闭包)

100018 · · 1750 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

匿名函数就是没有定义函数名称的函数。我们可以在函数内部定义匿名函数,也叫函数嵌套。

匿名函数可以直接被调用,也可以赋值给变量、作为参数或返回值。比如:

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
}

闭包在不用传递参数的情况下就可以读取和修改环境变量,当然我们是要为这种遍历付出代价的,所以日常开发中,在高并发服务

的场景下建议慎用,除非你明确你的需求必须这样做。

本文出自 “博学于文,约之于礼” 博客,转载请与作者联系!


有疑问加站长微信联系(非本文作者)

本文来自:51CTO博客

感谢作者:100018

查看原文:golang 函数二 (匿名函数和闭包)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1750 次点击  
加入收藏 微博
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传