go语言中函数可以作为返回值,可以作为参数,可以作为右值绑定到变量,golan把这些返回值,参数,或变量称为function value,函数指令在编译期间生成,而function value本质上是一个指针,指向一个runtime.funcval结构体,这个结构体里面只有一个地址——函数指令的入口地址。假设有如下代码:
func A(i int){
i++
fmt.Println(i)
}
func B(){
f1:= A
f1(1)
}
func C(){
f2:=A
f2(1)
}
f1和f2都指向同一个函数A(int),其指令入口为addr1,编译阶段编译器会在只读数据段为他分配一个funcval结构体fn指向addr1,而他本身的地址会在执行阶段赋给f1和f2,既然只要通过addr1就可以执行函数A(),为什么还要通过fn这个结构体中转一下呢,这是为闭包准备的。
什么是闭包(closure),用一句话来描述就是,闭包是一个函数和与他绑定的外部环境的集合。闭包在实现上是一个结构体,他存储了一个函数(通常是其入口地址)和一个关联的环境(相当于一个符号查找表)。环境中包含该函数的内部绑定符号,及其在外部定义但在函数中引用的自由变量。函数和闭包的不同在于,当捕获闭包的时候,他的自由变量会在捕捉时被确定,这样即使脱离了捕捉时的上下文,他也能照样运行。例如下面的例子:
func create() func()int{
c:=2
return func()int{
return c
}
}
func main(){
f1:=create()
f2:=create()
fmt.Println(f1())
fmt.Println(f2())
}
create() func()int
func create() []func() int {
function := make([](func() int), 2, 2)
for i := 0;i < 2;i++ {
function[i] = func() int {
return i
}
i++
}
return function
}
func main() {
funcs := create()
fmt.Println(funcs[0]()) \\ 2
fmt.Println(funcs[1]()) \\ 2
}
funcs[0]()funcs[1]()i