Go 编程 | 连载 24 - 闭包 Closure
一、闭包 Closure
闭包 Closure 在某些语言如 Java、Python 或者 Ruby 中也被成为 Lambda 表达式或者匿名函数。
闭包是引用了自由变量的 匿名函数
,被引用的自由变量和函数一同存在,即使已经离开了自由变量的环境也不会释放或者删除,在闭包中可以继续使用这个变量,也就是说闭包就等于 函数+引用环境
。
在 Go 编程 | 连载 23 - 函数实现接口 中,我们知道函数可以通过定义一个函数类型来像结构体一样实例化;函数本身不存储任何信息,只有与引用环境结合后形成的闭包才具有“记忆性”。
函数是编译期静态的概念,而闭包是运行期动态的概念。
闭包可以对作用域上变量的引用进行修改,修改引用的变量就会对变量进行实际修改。
package main
import "fmt"
func main(){
info := "Go"
// 将一个匿名函数赋值给 f 变量
f1 := func(){
// 修改 info 变量的值
info = "Elixir"
}
// 调用匿名函数
f1()
fmt.Println(info)
stark := make(map[string]interface{})
stark["name"] = "Tony Stark"
stark["age"] = 33
f2 := func() {
stark["address"] = "NYC"
}
f2()
fmt.Println(stark)
}
执行上述代码,输出结果如下:
Elixir
map[address:NYC age:33 name:Tony Stark]
二、闭包实现生成器
被捕获到比包中的变量让函数本身有了 “记忆”,闭包中的逻辑可以修改捕获的变量的值,变量就会跟随闭包的声明周期一直存在,闭包本身就如同变量一样有了 “记忆”。
package main
import "fmt"
func main() {
// 创建一个累加器,初始值为 1
outputSum := OutputSum(1)
fmt.Println(outputSum())
fmt.Printf("%p\n", outputSum)
// 创建一个累加器,初始值为 10
outputSum2 := OutputSum(10)
fmt.Println(outputSum2())
fmt.Printf("%p\n", outputSum2)
}
// 返回值是一个 func() int 类型既一个返回 int 类型的函数
func OutputSum(v int) func() int {
return func() int {
v ++
return v
}
}
执行上述代码,输出结果如下:
2
0x108b1a0
11
0x108b180
创建的两个函数实例虽然包含的变量不同,但其实是指向同一块内存,闭包会在运行时动态修改变量值。
闭包这种记忆的特性可以用于实现类似工厂模式的生成器。
package main
import "fmt"
func main() {
genScore := GenScore("Stark")
// 返回分数信息
name, score := genScore()
fmt.Println(name, score)
}
func GenScore(name string) func()(string, int) {
// score
score := 90
return func() (string, int){
return name, score
}
}
执行上述代码,输出结果如下:
Stark 90
上述代码中 GenScore 返回值将 name 和 score 两个变量引入匿名函数,从而形成闭包。
闭包还具有一定的封装性,函数外部无法直接访问修改这些匿名函数中引用的变量,这与面向对象中的封装特性相似。
转载自:https://juejin.cn/post/7133986985907060743