很好,这个问题问得非常扎实 👍闭包是很多并发、回调、框架设计的基础。
我用通俗版 + 技术版 + 应用场景三层解释。
函数在创建时:
这个“带着环境的函数”就是闭包。
func outer() func() { x := 10 return func() { fmt.Println(x) } }
这里:
x
所以:
f := outer() f() // 10
outer 已经结束了但 x 还活着
这就是闭包。
闭包 =
函数 + 它创建时的词法环境(变量作用域)
当匿名函数引用了外层变量:
func() { fmt.Println(x) }
Go 会:
所以 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) }
你代码里:
LLMStream(text, ctx, func(chunk string) { s.sendJson(...) })
这个匿名函数:
它“记住了”当前 session。
defer func() { log.Println(name) }()
匿名函数记住了 name。
func Subscribe(name string) func() { return func() { delete(m, name) } }
返回的函数记住了 name。
这就是闭包的经典应用。
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”。
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:
所以可以在未来某个时刻删除对应的事件。
这就是闭包在事件系统中的典型应用。
如果你愿意,我可以再讲一层更深的:
你现在已经开始写“框架级代码”,理解闭包会让你并发能力提升一个档次 🚀
Github开源生信云平台 DEMO