编程中闭包的概念


🧠 一句话定义(简化版):

闭包是一个函数,它“记住”了它创建时的环境(变量)。


🍱 生活类比:饭盒里的便当(闭包)

想象你去上班,带了个便当盒(函数):

  • 饭盒里放了饭菜(外部变量)
  • 虽然你已经离开了厨房(函数定义的作用域),
  • 但你打开便当盒,饭菜还在!你可以继续吃!

➡️ 闭包就是这样的便当盒:带着“当时的变量环境”走了。


🧪 编程版:一个 Go 的闭包示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func counter() func() int {
x := 0
return func() int {
x++
return x
}
}

func main() {
c := counter()
fmt.Println(c()) // 输出 1
fmt.Println(c()) // 输出 2
fmt.Println(c()) // 输出 3
}

💡 发生了什么?

  • counter 返回一个匿名函数,这个函数访问了变量 x
  • xcounter() 里定义的局部变量
  • 即使 counter() 执行完了,x 依然活着
  • 每次调用 c()x 都会 +1,因为闭包记住了它的值

✅ 用图感受一下

1
2
3
4
5
6
7
8
counter()
└── 创建变量 x = 0
└── 返回 func() {
x++
return x
}

c() ← 调用这个闭包,每次都会用到“记住的 x”

🧩 再举一个真实用途:生成器

1
2
3
4
5
6
7
8
9
10
11
12
13
func makeAdder(n int) func(int) int {
return func(x int) int {
return x + n
}
}

func main() {
add5 := makeAdder(5)
fmt.Println(add5(10)) // 输出 15

add10 := makeAdder(10)
fmt.Println(add10(10)) // 输出 20
}

🧠 解释:

  • makeAdder(5) 返回的函数里“记住了 n = 5”
  • 即使 makeAdder 函数早就结束了,这个返回的闭包仍然能使用 n 的值

🧾 和普通函数的区别

特点 普通函数 闭包(closure)
是否依赖外部变量 ❌ 否 ✅ 是,能“记住”上下文
生命周期限制 当前调用内 可能延续外部函数作用域之后
实际用途 逻辑分离 保持状态、记忆值、回调函数等

🏁 总结一波

闭包 = 函数 + 它创建时的环境变量的“快照”

它在很多地方都特别有用,比如:

  • 生成器函数
  • 回调函数
  • 保存状态的函数工厂
  • 柯里化(函数定制)