闭包是由函数及其相关引用环境组合而成的实体(即:闭包=函数+引用环境)。
Go中的闭包
func f() func() int {
i:=0
return func() int {
i++
return i
}
}
复制代码
函数f返回了一个函数,返回的这个函数,返回的这个函数就是一个闭包。这个函数本身没有定义变量的,而是引用了它所在的环境(函数f)中的变量i。
c1 := f()
c2 := f()
fmt.Println(c1()) // output: 1
fmt.Println(c2()) // output: 1
fmt.Println(c2()) // output: 2
fmt.Println(c1()) // output: 2
复制代码
函数f每调用一次,就形成了一个新的环境,对应的闭包中,函数都是同一个函数,环境却是引用不同的环境。
闭包环境中引用的变量是不能够在栈上分配的,而是在堆上分配。因为如果引用的变量在栈上分配,那么该变量会跟随函数f返回之后回收,那么闭包函数就不可能访问未分配的一个变量,即未声明的变量,之所以能够再堆上分配,而不是在栈上分配,是Go的一个语言特性----escape analyze(能够自动分析出变量的作用范围,是否将变量分配堆上)。
闭包的底层实现
Go在底层使用类似结构体的形式表示一个闭包。
我们可以把上面的闭包表示成这样,即:
type Closure struct{
func()() //匿名函数地址,当然语法要求一定要有变量名,这里只是为了表达匿名的含义
i *int //引用的变量地址
}
复制代码
小结
-
Go语言支持闭包
-
Go语言能通过escape analyze识别出变量的作用域,自动将变量在堆上分配。将闭包环境变量在堆上分配是Go实现闭包的基础。
-
返回闭包时并不是单纯返回一个函数,而是返回了一个结构体,记录下函数返回地址和引用的环境中的变量地址。