烦恼一般都是想太多了。

0%

Go中的闭包

不止一次见到过闭包这种词了,以前在游戏做脚本的时候用Lua,其中就有闭包这个特性,但是确实没有仔细的研究过,最近在学习Go语言也看到了这个特性,才从新来理解一下。

定义

在Go语言中,是如下定义闭包的:
Go 函数可以是一个闭包。
闭包是一个函数值,它引用了其函数体之外的变量。
该函数可以访问并赋予其引用的变量的值,换句话说,该函数被“绑定”在了这些变量上。
例如,函数 adder 返回一个闭包。每个闭包都被绑定在其各自的 sum 变量上。

package main

import "fmt"

func adder() func(int) int {
sum := 0
return func(x int) int {
sum += x
return sum
}
}
func main() {
pos := adder()
for i := 0; i < 5; i++ {
fmt.Println(pos(i))
}
}

执行此函数,输出的结果是:
0
1
3
6
10

怎么理解闭包绑定

例中pos被绑定在了sum上,怎么去理解它呢。
看看维基百科的说明:

在计算机科学中,闭包(Closure)是词法闭包(LexicalClosure)的简称,是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。

也就是说,闭包两个定义前提是:

  1. 引用了自由变量
  2. 函数

adder()返回一个闭包pos, 它引用了变量sumsum的生命周期由于返回函数的引用而变长。
再来进行更细一步的探究:
我们来打印pos的值和类型,因为它是一个闭包,看看会出现什么。
我们来尝试打印sum的地址。

...
sum += x
fmt.Println(&sum)
...
pos := adder()
fmt.Printf("%v-%T", pos, pos)
...

然后看一下输出是什么:

0x401290-func(int) int0xc04203a1d0
0xc04203a1d0
0
0xc04203a1d0
1
0xc04203a1d0
3
0xc04203a1d0
6
0xc04203a1d0
10

pos的值是一个地址,其类型是一个函数,返回值地址是0xc04203a1d0,就是adder()返回的函数。
sum变量一直存在,每次调用都会增加。

最后的理解

闭包是一个函数,由一个变量指向其地址,其返回值是固定地址值。
而每次闭包都引用都可以改变其引用变量的值。