Go 支持将函数当作值来使用,也允许定义匿名函数 ,并通过闭包实现对外部变量的捕获与持续访问。这一特性使函数式编程风格在 Go 中成为可能。
一、什么是匿名函数?
匿名函数是没有名字的函数,可以定义后立即调用,或赋值给变量后使用。
示例 1:定义后立即调用
result := func(a, b int) int {
return a + b
}(3, 5)
fmt.Println("结果:", result) // 输出:结果:8
示例 2:赋值给变量使用
add := func(x, y int) int {
return x + y
}
fmt.Println(add(10, 20)) // 输出:30
二、什么是闭包?
闭包是一个函数值 ,它"捕获 "并"记住"了其外部作用域的变量,即使外部函数已经执行完毕,这些变量依然存在。
示例:返回一个累加器
func counter() func() int {
i := 0
return func() int {
i++
return i
}
}
c := counter()
fmt.Println(c()) // 1
fmt.Println(c()) // 2
fmt.Println(c()) // 3
虽然 counter()
函数早已返回结束,但内部变量 i
依然"活着",被返回的函数持续访问并修改。
三、闭包的常见应用场景
1. 状态保存器
func makeSuffix(suffix string) func(string) string {
return func(name string) string {
if !strings.HasSuffix(name, suffix) {
return name + suffix
}
return name
}
}
addTxt := makeSuffix(".txt")
fmt.Println(addTxt("file")) // 输出:file.txt
fmt.Println(addTxt("log.txt")) // 输出:log.txt
2. 工厂函数生成器
可以生成具有自定义行为的函数,非常适合用于函数式编程。
四、闭包对并发的影响
闭包在与 goroutine 配合使用时需注意变量捕获问题:
for i := 0; i < 3; i++ {
go func() {
fmt.Println(i)
}()
}
输出可能为 3 3 3
,因为所有闭包共享同一个 i
。
✅ 正确方式:
for i := 0; i < 3; i++ {
i := i // 创建新的 i
go func() {
fmt.Println(i)
}()
}
五、小结
概念 | 特点说明 |
---|---|
匿名函数 | 没有名字,可赋值变量或立即调用 |
闭包 | 捕获其外部变量并持续访问 |
应用 | 状态保持器、工厂函数、自定义行为函数等 |
并发注意 | 闭包变量捕获共享问题,需谨慎使用 |
匿名函数和闭包让 Go 拥有更高阶的抽象能力,是构建灵活、高效逻辑的利器。