匿名函数与闭包 #

一、匿名函数 #

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回收,直到闭包被释放

六、总结 #

匿名函数与闭包要点:

特性 说明
匿名函数 没有名字的函数
闭包 引用外部变量的函数
捕获引用 闭包捕获变量的引用
共享变量 多个闭包可以共享变量

关键点:

  1. 匿名函数:可以立即执行或赋值给变量
  2. 闭包原理:捕获外部变量的引用
  3. 循环陷阱:注意循环变量捕获问题
  4. goroutine:在goroutine中使用闭包要特别小心
  5. 内存管理:闭包会持有外部变量引用

准备好学习defer语句了吗?让我们进入下一章!

最后更新:2026-03-26