闭包是 Go 中非常重要的概念,允许函数访问和操作其外部作用域的变量。理解闭包对于实现回调、累加器、工厂函数等场景非常有用。
一、闭包的概念
闭包是一个函数,它可以捕获并使用定义在其外部作用域的变量,即使外部函数已经返回,这些变量依然存在于内存中。
简单理解:
txt
闭包 = 函数 + 它捕获的外部变量
二、基本示例
go
package main
import "fmt"
func main() {
add := func(a, b int) int {
return a + b
}
fmt.Println(add(3, 5)) // 8
}
这是最基本的函数,也可以看作闭包,但没有捕获外部变量。
三、闭包捕获外部变量
go
package main
import "fmt"
func adder() func(int) int {
sum := 0
return func(x int) int {
sum += x
return sum
}
}
func main() {
f := adder()
fmt.Println(f(1)) // 1
fmt.Println(f(2)) // 3
fmt.Println(f(3)) // 6
}
说明:
adder返回一个函数- 返回的函数可以访问
adder内部的sum - 每次调用返回函数,
sum都会累加,闭包保存了sum的状态
四、闭包注意事项
- 捕获变量是引用
go
func main() {
funcs := []func(){}
for i := 0; i < 3; i++ {
funcs = append(funcs, func() {
fmt.Println(i)
})
}
for _, f := range funcs {
f() // 输出 3 3 3,而不是 0 1 2
}
}
原因:
- 闭包捕获的是
i的地址,循环结束后i = 3,所以每个闭包都打印 3。
解决办法:
go
for i := 0; i < 3; i++ {
x := i
funcs = append(funcs, func() {
fmt.Println(x)
})
}
输出:
0
1
2
- 闭包可以作为回调函数
go
func callback(f func(int) int) {
fmt.Println(f(10))
}
func main() {
y := 5
callback(func(x int) int {
return x + y
}) // 输出 15
}
闭包可以捕获外部变量 y。
- 闭包的应用场景
- 累加器(上例)
- 工厂函数(生成自定义函数)
- 回调函数
- 延迟执行或装饰器模式
五、闭包总结
- 闭包 = 函数 + 捕获的外部变量
- 捕获的变量在闭包内是引用,修改会影响外部变量
- 循环中使用闭包需注意变量捕获问题
- Go 中闭包常用于累加器、回调和函数工厂
- 理解闭包是深入掌握 Go 函数式编程风格的关键