go语言中的闭包
闭包/Closure 在维基百科中的解释为:
是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。
语法糖与设计模式
闭包实际上就是一种语法糖机制,而这种语法糖机制可以简化编程,从而可以提高代码的可读性。
比如,有时候对外部的局部变量进行访问,没这种语法糖机制将会编写冗余的代码。而这正也是可以把这种闭包机制归结为一种设计模式。
这种设计模式的好处就是可以很轻易的访问一个函数的内部的局部变量,再简单点就是通过函数嵌套的方式,闭包可以很轻易的实现函数内部和外部之间的连接。
我们知道函数是一段可执行代码,编译后就失效了,而这些函数在编译时就确定了,在执行时,不会发生变化,每个函数在内存中只有一份实例。而闭包并且在执行时候根据不同的引用变量和函数组合起来可以发生变化,也就意味着可以产生多个实例。还有一个好处就是函数调用结束时就会自动失效,而闭包的好处就是可以让这些变量始终保持在内存中,不会随着函数的调用而消失。
函数值
Go语言中不允许函数嵌套定义,但是可以用匿名函数来实现嵌套。在这里就得知道,在Go语言中,函数也是一种类型,这意味着可以把函数当成一个值来传递和返回。函数既可以作为一种返回类型又可以作为其他函数的参数。所以,这样很容易使用函数类型来实现闭包。
//如果写成
//var visitAll func(items []string){...} 报错
var visitAll func(items []string)
visitAll = func(items []string){//匿名函数
for _,item := range items{
if !visited[item] {
visited[item] = true
visitAll(m[item])
//递归入栈
order = append(order,item)
}
}
}
闭包包了什么?
包了函数和环境,
函数指外部函数的内部函数。环境指闭包保存/记录了它产生时的外部函数的所有环境。
看一个例子:
func foo(x int) func() {
return func() {
x = x + 1
fmt.Printf("foo2 val = %d
", x)
}
}
环境就是指内部函数(匿名函数)用到的不属于它的变量x
。
闭包延迟绑定
在执行的时候去外部环境寻找最新的数值,
func foo(x int) []func() {
var fs []func()
values := []int{1, 2, 3, 5}
val:=0
for _, val = range values {
fs = append(fs, func() {
fmt.Printf("foo val = %d
", x+val)
})
}
//val:=11
return fs
}
// Q4实验:
fs := foo(11)
for _, f := range fs {
f()
}
输出:
foo val = 16
foo val = 16
foo val = 16
foo val = 16
这就是所谓的 闭包延迟绑定,就是指,闭包虽然包了环境,但是包的是最新的值,当真正运行闭包函数的时候,函数再去环境找值,所以采用的上述例子中,闭包函数中的 val
一直是5。如果我们把函数中注释掉的那句 val = 11
那么输出的就会是22了。