go语言学习笔记 — 基础 — 函数:闭包(closure)—— 引用了外部变量的匿名函数
Posted Locutus
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了go语言学习笔记 — 基础 — 函数:闭包(closure)—— 引用了外部变量的匿名函数相关的知识,希望对你有一定的参考价值。
闭包是引用了(函数外部)自由变量的函数。被引用的自由变量和函数一同存在,即使离开了自由变量所在的引用环境(例如在某个go源文件中导入某个package的闭包),我们仍可在闭包中继续使用这个自由变量。自由变量是闭包函数外部作用域中的变量,可能是全部变量、函数入参、局部变量。
简而言之,闭包 = 函数 + 引用环境
。同一个函数可以与不同引用环境结合,形成不同的闭包实例。
闭包具有以下特点:
- 函数像结构体一样,可以被实例化成一个闭包
- 函数本身并不存储任何数据,只有与引用环境相结合形成闭包后,才具有“记忆性”
- 函数是编译时的静态概念,而闭包是运行时的动态概念
1. 在闭包内部修改引用的变量
函数引用其变量作用域上的变量,形成闭包。修改闭包内的引用变量会对自由变量进行实际修改。
package func_test
import (
"fmt"
"testing"
)
// 准备一个字符串
var str = "hello world"
// 创建一个匿名函数
var foo = func() {
str = "hello china"
fmt.Println(str)
}
func TestClosure(t *testing.T) {
foo()
fmt.Println(str)
}
自由变量str不是在匿名函数中,而是在匿名函数之前定义。自由变量str被引用到匿名函数中,形成闭包。修改闭包内的变量str,会修改自由变量。
2. 闭包的记忆效应
被引用的自由变量让闭包拥有了记忆效应。我们可以在闭包中修改所引用的自由变量,这个变量会和闭包一直存在,而闭包就像变量一样拥有记忆效应。
package func_test
import (
"fmt"
"testing"
)
// 定义累加器生成函数Accumulate
func Accumulate(value int) func() int {
return func() int { // 返回一个闭包,这个闭包是一个匿名函数引用了装饰者函数的入参
value++
return value // 返回被修改后的自由变量
}
}
func TestClosureMemory(t *testing.T) {
accumulator := Accumulate(1) // 创建一个闭包实例accumulator,引用入参1
fmt.Println(accumulator()) // 调用闭包实例,并打印闭包中被修改后的变量,证明闭包的记忆性
fmt.Println(accumulator())
fmt.Printf("%p\\n", accumulator) // 打印闭包的内存地址
accumulator2 := Accumulate(10) // 创建一个闭包实例accumulator,引用入参10
fmt.Println(accumulator2()) // 调用闭包实例,并打印闭包中被修改后的变量,证明闭包的记忆性
fmt.Printf("%p\\n", accumulator2) // 打印闭包的内存地址
}
3. 闭包实现生成器
package func_test
import (
"fmt"
"testing"
)
// 创建一个玩家生成器,输入名称,输出生成器
func playGen(name string) func() (string, int) {
hp := 150 // hp是playGen函数的局部变量(不能被playGen函数的外部访问和修改),也是被匿名函数引用的自由变量。
return func() (string, int) { // 返回匿名函数
return name, hp // 匿名函数引用外部变量name和hp,形成闭包
}
}
func TestGenerator(t *testing.T) {
// 生成一个玩家生成器实例(闭包)
generator := playGen("high noon")
name, hp := generator() // 调用闭包,获取记忆变量
fmt.Println(name, hp)
}
以上是关于go语言学习笔记 — 基础 — 函数:闭包(closure)—— 引用了外部变量的匿名函数的主要内容,如果未能解决你的问题,请参考以下文章
go语言学习笔记 — 基础 — 基本数据类型 — 字符串:字符串初始化声明赋值
go语言学习笔记 — 基础 — 基本数据类型 — 字符串:字符串初始化声明赋值
1.4 Go微服务实战(Go语言基础) --- 函数方法接口和反射