9.6Go之函数之闭包

9.6Go之函数之闭包

闭包的概念

什么是闭包?

闭包就是能够读取其他函数内部变量的函数。通过变量的定义域可以理解为:定义在其他函数内部的函数。

本质上闭包是将函数内部和函数外部连接起来的桥梁

闭包的特点

  • 闭包包含自由(未绑定到特定对象)变量,这些变量不是在这个代码块内或者任何全局上下文中定义,而是在定义代码块的环境中定义(局部变量)

闭包的组成

  • 代码块(自由变量被包含在代码块中,这些自由变量和它们引用的对象没有被释放)

  • 变量的作用域

闭包是引用了自由变量的函数--->相当于内部函数,引用了外部函数的局部变量

函数 + 引用环境 = 闭包

由此可知:同一个匿名函数,引用环境不同形成的闭包不同

函数的特点

  • 函数类型就像结构体一样,可以被实例化

  • 函数本身不存储任何信息,只有与引用环境结合后形成的闭包才具有“记忆性”

  • 函数是编译期静态的概念,闭包是运行期动态的概念

Java中闭包成为Lambda表达式

闭包的三个示例

  • 在闭包的内部修改引用的变量--->直接改掉了变量的内容,闭包可以直接赋值给变量

  • 闭包的记忆效应

  • 闭包实现生成器


闭包内部修改引用的变量:

package main

import "fmt"

func main() {
//定义一个字符串
str := "Hello World"

//在这里先打印一遍str与后面的str打印做对比
fmt.Println(str)

//创建一个匿名函数
foo := func() {
//匿名函数中访问外部函数变量
str = "Hello JunkingBoy"
       /*
       匿名函数中并没有定义 str,str 的定义在匿名函数之前,此时,str 就被引用到了匿名函数中形成了闭包。
       */
}

//调用匿名函数
foo() //这里调用str的值会被修改
   /*
   执行闭包,此时 str 发生修改,变为 hello dude。
   */

//打印出str
fmt.Println(str)
}

利用闭包实现记忆效应:--->简单来说就是通过闭包修改了一个变量可以执行与外部函数不一样的逻辑

package main

import "fmt"

/*
可以称为一个外函数
*/
func Accumulate(value int) func() int {
//返回一个内部函数,内部函数引用外部函数的形参value形成闭包
return func() int {
//引用value
value++
//返回value
return value
}
}

func main() {
//创建一个累加器,初始值为1
accumulate1 := Accumulate(1)

//打印变量
fmt.Println(accumulate1())

//打印变量的函数地址
fmt.Printf("Variable address is:%p ", &accumulate1)

//在实例化一个外函数
accumulate2 := Accumulate(10)

//打印变量
fmt.Println(accumulate2())

//打印变量地址
fmt.Printf("Variable address No2 is:%p ", &accumulate2)
fmt.Printf("Variable address is:%p ", &accumulate1)
}

代码分析:

  • func Accumulate(value int) func() int调用时返回一个为初始值创建的闭包函数

  • return func() int返回一个闭包函数,每次返回会创建一个新的函数实例

  • value++对引用的 Accumulate 参数变量进行累加。因为value不是匿名函数定义的是引用外部函数的,所以形成闭包

  • return value通过闭包返回修改值

  • accumulator := Accumulate(1)创建一个累加器,初始值为 1,返回的 accumulate1 是类型为 func()int 的函数变量

对比输出的日志发现 accumulaeaccumulate2 输出的函数地址不同,因此它们是两个不同的闭包实例

利用闭包实现生成器:--->类似设计模式中工厂模式的生成器。简单来说就是通过内部函数定义具体属性,将外部函数定义成变量

package main

import "fmt"

/*
定义一个玩家函数,里面定义内部函数通过闭包的方式返回变量值
@date 2021/09/06
@author Lucifer
*/
func playerGame(name string) func() (string, int) {
//定义hp
hp := 150
//通过闭包返回局部变量和外部函数的引用
return func() (string, int) {
return name, hp
/*
这里面的name是对外部函数形参的引用,hp是对外部函数的局部变量的引用
因此形成了闭包
*/
}
}

func main() {
//将外部函数定义成变量
player1 := playerGame("God")
//定义闭包中的两个返回值属性
name, hp := player1()
//打印这两个变量
fmt.Println(name,hp)
}

代码分析:

  • func playerGame(name string) func() (string, int) playerGame() 需要提供一个名字来创建一个玩家的生成函数--->传入一个属性,在内部定义其他属性,通过闭包的方式返回全部的属性

  • return func() (string, int)hpname 变量引用到匿名函数中形成闭包

  • player1 := playerGame("God")通过 playerGame传入参数调用后获得玩家生成器

  • name, hp := player1()这里是调用生成器,上面是获取生成器。调用生成器获得属性

闭包还具有一定的封装性, playerGame的局部变量是hp,playerGame的外部无法直接访问及修改这个变量,与面向对象中强调的封装性类似。

It's a lonely road!!!
原文地址:https://www.cnblogs.com/JunkingBoy/p/15235300.html