通俗的解释闭包,以及闭包的应用

最后发布时间:2026-02-12 15:17:43 浏览量:

很好,这个问题问得非常扎实 👍
闭包是很多并发、回调、框架设计的基础。

我用通俗版 + 技术版 + 应用场景三层解释。

一、什么是闭包(通俗版)

🎒 想象一个“小书包”

函数在创建时:

  • 不仅带着代码
  • 还会“背上”当时环境里的变量

这个“带着环境的函数”就是闭包。

🍎 举个生活例子

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

这里:

  • x 本来属于 outer
  • outer 结束后,按理说 x 应该消失
  • 但返回的函数“背走了 x”

所以:

f := outer()
f()   // 10

outer 已经结束了
但 x 还活着

这就是闭包。

二、技术版解释

闭包 =

函数 + 它创建时的词法环境(变量作用域)

当匿名函数引用了外层变量:

func() {
fmt.Println(x)
}

Go 会:

  • 把 x 放到堆上
  • 让匿名函数持有对 x 的引用

所以 x 不会被销毁。

三、闭包捕获的是什么?

⚠️ 关键点:

闭包捕获的是“变量”,不是“变量的值”

🚨 经典坑

for i := 0; i < 3; i++ {
go func() {
fmt.Println(i)
}()
}

你可能以为输出:

0 1 2

但实际可能是:

3 3 3

因为:

所有 goroutine 共享同一个 i。

✅ 正确写法

for i := 0; i < 3; i++ {
i := i
go func() {
fmt.Println(i)
}()
}

或:

for i := 0; i < 3; i++ {
go func(i int) {
fmt.Println(i)
}(i)
}

四、闭包的常见应用

1️⃣ 回调函数

你代码里:

LLMStream(text, ctx, func(chunk string) {
s.sendJson(...)
})

这个匿名函数:

  • 用到了 s
  • 用到了 ctx

这就是闭包。

它“记住了”当前 session。

2️⃣ 延迟执行(defer)

defer func() {
log.Println(name)
}()

匿名函数记住了 name。

3️⃣ 取消函数(你代码里的 unsubscribe)

func Subscribe(name string) func() {
return func() {
delete(m, name)
}
}

返回的函数记住了 name。

这就是闭包的经典应用。

4️⃣ 工厂模式

func Adder(base int) func(int) int {
return func(x int) int {
return base + x
}
}

使用:

add10 := Adder(10)
add20 := Adder(20)

fmt.Println(add10(5)) // 15
fmt.Println(add20(5)) // 25

不同的闭包“记住不同的 base”。

5️⃣ 中间件模式(Web 框架核心)

func Logger(next Handler) Handler {
return func(ctx Context) {
log.Println("before")
next(ctx)
log.Println("after")
}
}

闭包让 next 被“包起来”。

五、为什么闭包这么重要?

因为它让你:

  • 写出函数式风格代码
  • 实现回调
  • 实现事件系统
  • 实现中间件
  • 实现协程逻辑封装

在并发系统中几乎无处不在。

六、闭包的优缺点

✅ 优点

  • 代码优雅
  • 能封装状态
  • 支持回调
  • 方便异步

❌ 缺点

  • 容易捕获错误变量
  • 可能导致变量逃逸到堆(性能影响)
  • 容易隐藏生命周期问题

七、一句话总结

闭包就是:

一个函数“记住”它出生时周围的变量。

或者更形象一点:

函数带着它的“过去”一起活着。

八、结合你现在的架构

你代码里的:

unsub := func() {
delete(b.eventName, name)
}

这个 unsub:

  • 记住了 name
  • 记住了 b

所以可以在未来某个时刻删除对应的事件。

这就是闭包在事件系统中的典型应用。

如果你愿意,我可以再讲一层更深的:

  • 闭包在 Go 里的“逃逸分析”
  • 为什么闭包会导致变量上堆
  • 闭包在高性能系统里的注意事项
  • 如何避免闭包引起的内存增长

你现在已经开始写“框架级代码”,理解闭包会让你并发能力提升一个档次 🚀