一:什么是闭包?先理解概念

闭包 = 函数 + 它“记住”的外部变量

打个比方:

  • 你有一个函数(内部函数)

  • 这个函数用到了外面函数里的变量

  • 当外面函数返回这个内部函数时,这个内部函数一直记得那些变量

二:Go 中的闭包实现

Go 通过匿名函数 + 外部变量引用实现闭包。

基础语法

func 外部函数() func() 返回类型 {
    变量 := 初始值
    return func() 返回类型 {
        // 使用外部变量
        变量...
        return 变量
    }
}

三:第一个例子:计数器

package main

import "fmt"

// 创建一个计数器函数
func createCounter() func() int {
    count := 0  // 这个变量会被闭包记住
    
    // 返回匿名函数(闭包)
    return func() int {
        count++  // 修改外部变量
        return count
    }
}

func main() {
    // 创建两个独立的计数器
    counter1 := createCounter()
    counter2 := createCounter()
    
    fmt.Println(counter1()) // 1
    fmt.Println(counter1()) // 2
    fmt.Println(counter1()) // 3
    
    fmt.Println(counter2()) // 1(独立的,重新开始)
    fmt.Println(counter2()) // 2
}

解释

  • count 是 createCounter 的局部变量

  • 返回的函数“记住”了 count

  • 每次调用返回的函数,count 都会保留上次的值

  • 不同的调用创建不同的“记忆”

四:第二个例子:累加器(带初始值)

package main

import "fmt"

// 创建累加器,可以设置初始值
func makeAccumulator(initial int) func(int) int {
    sum := initial
    
    return func(add int) int {
        sum += add
        return sum
    }
}

func main() {
    // 从 10 开始累加
    acc := makeAccumulator(10)
    
    fmt.Println(acc(5))  // 15 (10+5)
    fmt.Println(acc(3))  // 18 (15+3)
    fmt.Println(acc(2))  // 20 (18+2)
}

五:实际应用场景

场景1:中间件(权限验证)

package main

import "fmt"

// 创建权限验证中间件
func authMiddleware(role string) func(string) bool {
    // 闭包记住需要的角色
    return func(userRole string) bool {
        return userRole == role
    }
}

func main() {
    // 创建不同的权限验证器
    adminAuth := authMiddleware("admin")
    userAuth := authMiddleware("user")
    
    fmt.Println(adminAuth("admin")) // true(管理员有权限)
    fmt.Println(adminAuth("guest")) // false(游客没权限)
    
    fmt.Println(userAuth("user"))   // true
    fmt.Println(userAuth("admin"))  // false
}

场景2:数据缓存/记忆函数

package main

import "fmt"

// 创建一个带缓存的斐波那契计算器
func makeFibonacci() func(int) int {
    cache := make(map[int]int)  // 闭包记住这个缓存
    
    var fib func(int) int
    fib = func(n int) int {
        // 先从缓存查找
        if val, exists := cache[n]; exists {
            return val
        }
        
        // 计算并存入缓存
        if n <= 1 {
            cache[n] = n
        } else {
            cache[n] = fib(n-1) + fib(n-2)
        }
        return cache[n]
    }
    
    return fib
}

func main() {
    fib := makeFibonacci()
    
    fmt.Println(fib(10)) // 55(计算并缓存)
    fmt.Println(fib(10)) // 55(直接从缓存取,很快)
    fmt.Println(fib(5))  // 5(已经从缓存得到)
}

场景3:迭代器生成器

package main

import "fmt"

// 生成偶数的迭代器
func evenNumberGenerator() func() int {
    current := 0
    
    return func() int {
        current += 2
        return current
    }
}

// 生成指定范围内的数字
func rangeGenerator(start, end int) func() (int, bool) {
    current := start
    
    return func() (int, bool) {
        if current > end {
            return 0, false
        }
        value := current
        current++
        return value, true
    }
}

func main() {
    // 偶数生成器
    nextEven := evenNumberGenerator()
    fmt.Println(nextEven()) // 2
    fmt.Println(nextEven()) // 4
    fmt.Println(nextEven()) // 6
    
    // 范围迭代器
    nextNum, hasNext := rangeGenerator(1, 3)
    
    for {
        if num, ok := nextNum(); ok {
            fmt.Println(num) // 1, 2, 3
        } else {
            break
        }
    }
}

场景4:延迟执行(装饰器模式)

package main

import (
    "fmt"
    "time"
)

// 计时装饰器
func timingDecorator(f func()) func() {
    return func() {
        start := time.Now()
        f()                 // 执行原函数
        elapsed := time.Since(start)
        fmt.Printf("执行耗时:%v\n", elapsed)
    }
}

// 日志装饰器
func logDecorator(f func(string), prefix string) func(string) {
    return func(msg string) {
        fmt.Printf("[%s] %s: %s\n", time.Now().Format("15:04:05"), prefix, msg)
        f(msg)
    }
}

func main() {
    // 原始函数
    work := func() {
        time.Sleep(1 * time.Second)
        fmt.Println("执行任务...")
    }
    
    // 添加计时功能(闭包记住了work函数)
    timedWork := timingDecorator(work)
    timedWork()
    
    // 添加日志功能
    printMsg := func(msg string) {
        fmt.Println("输出:", msg)
    }
    
    loggedPrint := logDecorator(printMsg, "INFO")
    loggedPrint("Hello World")
}

六:闭包的内存原理(通俗版)

func outer() func() {
    x := 10
    return func() {
        fmt.Println(x)
    }
}

发生了什么

  1. 创建 outer 函数时,x 分配在堆上(不是栈)

  2. 返回的匿名函数持有 x 的引用

  3. 即使 outer 执行完毕,x 不会被销毁

  4. 只要返回的函数还存在,x 就继续存活

七:常见陷阱和注意事项

陷阱1:循环变量问题

package main

import "fmt"

func main() {
    // ❌ 错误示例
    funcs := []func(){}
    for i := 0; i < 3; i++ {
        funcs = append(funcs, func() {
            fmt.Println(i)  // 所有闭包都引用同一个i
        })
    }
    
    for _, f := range funcs {
        f()  // 输出: 3 3 3(不是 0 1 2)
    }
    
    // ✅ 正确示例:创建副本
    funcs2 := []func(){}
    for i := 0; i < 3; i++ {
        i2 := i  // 创建新变量
        funcs2 = append(funcs2, func() {
            fmt.Println(i2)
        })
    }
    
    for _, f := range funcs2 {
        f()  // 输出: 0 1 2
    }
}

陷阱2:意外共享变量

package main

import "fmt"

func main() {
    // 错误:共享同一个变量
    var funcs []func()
    shared := 0
    for i := 0; i < 3; i++ {
        funcs = append(funcs, func() {
            shared += i
            fmt.Println(shared)
        })
    }
    
    // 正确:每次创建独立的闭包
    makeFunc := func(base int) func() {
        return func() {
            fmt.Println(base)
        }
    }
}

八:闭包 vs 普通函数对比

特性 普通函数 闭包
状态保持 需要全局变量或参数传递 自动记住外部变量
独立性 所有调用共享全局状态 每次创建独立状态
代码复用 通过参数配置 通过创建时配置
内存 函数执行完释放 闭包存在就保留状态

九:什么时候用闭包?

✅ 适合用的场景

  • 需要保持状态的小型功能(计数器、累加器)

  • 函数工厂(根据参数生成不同行为的函数)

  • 延迟执行或装饰器模式

  • 迭代器/生成器

  • 回调函数需要访问上下文

❌ 不适合的场景

  • 逻辑复杂、代码量大的功能(用结构体+方法更好)

  • 需要大量共享状态(用结构体更清晰)

  • 性能要求极高(闭包有额外内存开销)

十:总结

闭包的本质:函数 + 它所引用的外部变量的环境

Go 实现闭包的关键

  1. 匿名函数(func() { ... }

  2. 引用外部函数的变量

  3. 返回匿名函数

记忆口诀

  • 函数套函数,里用外变量

  • 返回里函数,记住外变量

  • 每次调用,独立状态

更多推荐