网络异常,图片无法展示
|
匿名函数
什么是匿名函数?字面意义上说明就是没有被赋予名称的函数,举个例子:
package main import ( "fmt" ) func main() { // 匿名函数定义方式1 func (s string){ fmt.Printf("匿名函数:%s\n",s) }("定义方式1") // 匿名函数定义方式2 fn:=func (s string){ fmt.Printf("匿名函数:%s\n",s) } fn("定义方式2") }
闭包
闭包则实际上是一个函数的实例,也就是说它是存在于内存里的某个结构体。如果从实现上来看的话,匿名函数如果没有捕捉自由变量,那么它其实可以被实现为一个函数指针,或者直接内联到调用点,如果它捕捉了自由变量那么它将是一个闭包;
而闭包则意味着同时包括函数指针和环境两个关键元素。在编译优化当中,没有捕捉自由变量的闭包可以被优化成普通函数,这样就无需分配闭包结构体,这种编译技巧被称为函数跃升。
闭包=函数+引用环境
package main import ( "fmt" ) func main() { // c拷贝闭包副本 c:=Closure() c() // 调用闭包 c() // 调用闭包 // c2会拷贝新的闭包副本 c2:=Closure() c2() // 调用闭包 } /* 闭包 */ func Closure() func(){ i:=1 return func() { i++ fmt.Printf("闭包,i=%d\n",i) } }
输出:
闭包,i=2 闭包,i=3 闭包,i=2
逃逸分析
go语言能通过escape analyze识别出变量的作用域,自动将变量在堆上分配。将闭包环境变量在堆上分配是Go实现闭包的基础。
可以通过如下指令来分析上面的代码:
go build --gcflags=-m main.go
输出:
# command-line-arguments ./main.go:23:13: inlining call to fmt.Printf ./main.go:20:2: moved to heap: i ./main.go:21:9: func literal escapes to heap ./main.go:23:14: i escapes to heap ./main.go:23:13: []interface {}{...} does not escape <autogenerated>:1: .this does not escape
可以看到**./main.go:23:14: i escapes to heap**,表明变量i
已经逃逸到堆上了。