匿名函数与闭包 #
一、匿名函数 #
1.1 基本概念 #
匿名函数是没有名字的函数,也称为函数字面量。
1.2 定义匿名函数 #
go
func(a, b int) int {
return a + b
}
1.3 立即执行 #
go
result := func(a, b int) int {
return a + b
}(1, 2)
fmt.Println(result) // 3
1.4 赋值给变量 #
go
add := func(a, b int) int {
return a + b
}
fmt.Println(add(1, 2)) // 3
1.5 作为参数 #
go
func apply(a, b int, f func(int, int) int) int {
return f(a, b)
}
result := apply(1, 2, func(a, b int) int {
return a + b
})
fmt.Println(result) // 3
1.6 作为返回值 #
go
func getAdder() func(int, int) int {
return func(a, b int) int {
return a + b
}
}
adder := getAdder()
fmt.Println(adder(1, 2)) // 3
二、闭包 #
2.1 闭包概念 #
闭包是一个函数值,它引用了函数体之外的变量。闭包可以访问和修改外部变量。
2.2 基本示例 #
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
2.3 闭包原理 #
闭包捕获外部变量的引用,而不是值:
go
func main() {
x := 10
f := func() {
fmt.Println(x) // 捕获x的引用
}
x = 20
f() // 20
}
2.4 多个闭包共享变量 #
go
func counter() (increment, decrement func() int) {
count := 0
increment = func() int {
count++
return count
}
decrement = func() int {
count--
return count
}
return
}
inc, dec := counter()
fmt.Println(inc()) // 1
fmt.Println(inc()) // 2
fmt.Println(dec()) // 1
三、闭包应用 #
3.1 工厂函数 #
go
func makeAdder(x int) func(int) int {
return func(y int) int {
return x + y
}
}
add5 := makeAdder(5)
add10 := makeAdder(10)
fmt.Println(add5(3)) // 8
fmt.Println(add10(3)) // 13
3.2 累加器 #
go
func accumulator(initial int) func(int) int {
sum := initial
return func(n int) int {
sum += n
return sum
}
}
acc := accumulator(0)
fmt.Println(acc(10)) // 10
fmt.Println(acc(20)) // 30
fmt.Println(acc(30)) // 60
3.3 斐波那契数列 #
go
func fibonacci() func() int {
a, b := 0, 1
return func() int {
a, b = b, a+b
return a
}
}
f := fibonacci()
for i := 0; i < 10; i++ {
fmt.Println(f())
}
3.4 中间件模式 #
go
func withLogging(f func(string) string) func(string) string {
return func(s string) string {
fmt.Printf("Input: %s\n", s)
result := f(s)
fmt.Printf("Output: %s\n", result)
return result
}
}
func process(s string) string {
return strings.ToUpper(s)
}
loggedProcess := withLogging(process)
loggedProcess("hello")
3.5 延迟计算 #
go
func lazyCompute(fn func() int) func() int {
var result int
var computed bool
return func() int {
if !computed {
result = fn()
computed = true
}
return result
}
}
expensive := lazyCompute(func() int {
fmt.Println("Computing...")
time.Sleep(time.Second)
return 42
})
fmt.Println("Created")
fmt.Println(expensive()) // Computing... 42
fmt.Println(expensive()) // 42 (cached)
四、闭包陷阱 #
4.1 循环变量陷阱 #
go
var 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。
4.2 解决方法 #
方法一:创建局部变量
go
for i := 0; i < 3; i++ {
i := i // 创建新变量
funcs = append(funcs, func() {
fmt.Println(i)
})
}
// 输出: 0 1 2
方法二:传参
go
for i := 0; i < 3; i++ {
funcs = append(funcs, func(n int) func() {
return func() {
fmt.Println(n)
}
}(i))
}
4.3 range中的陷阱 #
go
var prints []func()
for _, v := range []int{1, 2, 3} {
prints = append(prints, func() {
fmt.Println(v)
})
}
for _, p := range prints {
p()
}
// 输出: 3 3 3
解决方法:
go
for _, v := range []int{1, 2, 3} {
v := v
prints = append(prints, func() {
fmt.Println(v)
})
}
// 输出: 1 2 3
4.4 goroutine中的陷阱 #
go
for i := 0; i < 3; i++ {
go func() {
fmt.Println(i)
}()
}
// 可能输出: 3 3 3
// 解决方法
for i := 0; i < 3; i++ {
go func(n int) {
fmt.Println(n)
}(i)
}
五、最佳实践 #
5.1 避免长闭包 #
go
// 不好
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// 很多代码...
})
// 好
http.HandleFunc("/", handleRoot)
func handleRoot(w http.ResponseWriter, r *http.Request) {
// 处理逻辑
}
5.2 使用闭包简化代码 #
go
// 不使用闭包
func processItems(items []Item) {
for _, item := range items {
if item.IsValid() {
result := item.Process()
fmt.Println(result)
}
}
}
// 使用闭包
func processItems(items []Item) {
process := func(item Item) {
if item.IsValid() {
result := item.Process()
fmt.Println(result)
}
}
for _, item := range items {
process(item)
}
}
5.3 注意闭包内存 #
闭包会持有外部变量的引用,可能导致内存不释放:
go
func createClosure() func() {
largeData := make([]byte, 1<<30) // 1GB
return func() {
fmt.Println(len(largeData)) // 闭包持有largeData引用
}
}
// largeData不会被GC回收,直到闭包被释放
六、总结 #
匿名函数与闭包要点:
| 特性 | 说明 |
|---|---|
| 匿名函数 | 没有名字的函数 |
| 闭包 | 引用外部变量的函数 |
| 捕获引用 | 闭包捕获变量的引用 |
| 共享变量 | 多个闭包可以共享变量 |
关键点:
- 匿名函数:可以立即执行或赋值给变量
- 闭包原理:捕获外部变量的引用
- 循环陷阱:注意循环变量捕获问题
- goroutine:在goroutine中使用闭包要特别小心
- 内存管理:闭包会持有外部变量引用
准备好学习defer语句了吗?让我们进入下一章!
最后更新:2026-03-26