Go匿名函数与闭包
Go匿名函数无需命名,闭包可捕获并持有外部变量。
匿名函数
定义与调用
Go
// 定义并立即调用
func() {
fmt.Println("匿名函数")
}()
// 带参数
func(a, b int) {
fmt.Println(a + b)
}(1, 2)
// 带返回值
result := func(a, b int) int {
return a + b
}(1, 2)
赋值给变量
Go
// 匿名函数赋给变量
add := func(a, b int) int {
return a + b
}
// 作为普通函数调用
result := add(1, 2)
作为参数传递
Go
// 函数作为参数
func process(fn func(int) int, n int) int {
return fn(n)
}
// 传递匿名函数
result := process(func(n int) int {
return n * 2
}, 5)
fmt.Println(result) // 10
闭包
基本概念
Go
// 闭包:函数 + 它引用的外部变量
func counter() func() int {
count := 0 // 外部变量
return func() int {
count++ // 引用并修改外部变量
return count
}
}
c := counter()
fmt.Println(c()) // 1
fmt.Println(c()) // 2
fmt.Println(c()) // 3
闭包"记住"并持有外部变量,每次调用共享同一变量。
闭包持有变量
Go
func adder(base int) func(int) int {
return func(n int) int {
return base + n // 持有base变量
}
}
add5 := adder(5)
add10 := adder(10)
fmt.Println(add5(3)) // 8 (5 + 3)
fmt.Println(add10(3)) // 13 (10 + 3)
// add5和add10持有不同的base
闭包陷阱
循环变量捕获
Go
// 陷阱:所有闭包引用同一变量
for i := 0; i < 3; i++ {
defer func() {
fmt.Println(i) // 可能打印 3, 3, 3
}()
}
// 解决:传递参数
for i := 0; i < 3; i++ {
defer func(n int) {
fmt.Println(n) // 打印 2, 1, 0
}(i)
}
goroutine中的闭包
Go
// 陷阱:共享变量
for i := 0; i < 3; i++ {
go func() {
fmt.Println(i) // 可能全打印3
}()
}
// 解决:传递参数
for i := 0; i < 3; i++ {
go func(n int) {
fmt.Println(n) // 打印0, 1, 2
}(i)
}
循环变量捕获时,闭包引用变量本身而非值,循环结束所有闭包看到最终值。
闭包应用场景
状态保持
Go
func accumulator() func(int) int {
total := 0
return func(n int) int {
total += n
return total
}
}
acc := accumulator()
fmt.Println(acc(10)) // 10
fmt.Println(acc(20)) // 30
fmt.Println(acc(30)) // 60
配置封装
Go
func makeGreeter(greeting string) func(string) string {
return func(name string) string {
return greeting + name
}
}
hello := makeGreeter("Hello, ")
hi := makeGreeter("Hi, ")
fmt.Println(hello("Tom")) // Hello, Tom
fmt.Println(hi("Tom")) // Hi, Tom
匿名函数与闭包对比
| 特性 | 匿名函数 | 闭包 |
|---|---|---|
| 定义 | 无名称函数 | 函数 + 外部变量 |
| 变量捕获 | 不捕获 | 捕获并持有 |
| 状态 | 无状态 | 有状态 |
| 多次调用 | 独立执行 | 共享外部变量 |
要点总结
- 匿名函数无需命名,可立即调用或赋值
- 闭包捕获并持有外部变量
- 闭包多次调用共享同一外部变量
- 循环中闭包陷阱:引用同一变量
- 解决闭包陷阱:传递参数而非引用
- goroutine中闭包同样有陷阱
- 闭包用于状态保持、配置封装
📝 发现内容有误?点击此处直接编辑