likes
comments
collection
share

Go 编程 | 连载 24 - 闭包 Closure

作者站长头像
站长
· 阅读数 61

一、闭包 Closure

闭包 Closure 在某些语言如 Java、Python 或者 Ruby 中也被成为 Lambda 表达式或者匿名函数。

闭包是引用了自由变量的 匿名函数,被引用的自由变量和函数一同存在,即使已经离开了自由变量的环境也不会释放或者删除,在闭包中可以继续使用这个变量,也就是说闭包就等于 函数+引用环境

Go 编程 | 连载 24 - 闭包 Closure

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
评论
请登录