闭包是高级编程语言中的一个常用概念,也是一个比较难以理解的概念。
要解释闭包先要从匿名函数开始
匿名函数
匿名函数跟普通函数是一样的只是他没有名字。举个例子可以形象的说明
下面的例子是一个普通的函数定义
func DoStuff() {
// Do stuff
}
如果我们想把他变成匿名函数,我们首先声明一个func类型的变量,然后就可以把任何匿名函数赋值给它了
var DoStuff func() = func() {
// Do stuff
}
这两个函数其中一个区别就是匿名函数可以运行的时候随意改变里面的内容。
package main
import "fmt"
var DoStuff func() = func() {
// Do stuff
fmt.Println("Doing MAIN stuff!")
}
func main() {
DoStuff()
DoStuff = func() {
fmt.Println("Doing stuff!")
}
DoStuff()
DoStuff = func() {
fmt.Println("Doing other stuff.")
}
DoStuff()
}
运行结果是:
Doing MAIN stuff!
Doing stuff!
Doing other stuff.
闭包
闭包是匿名函数的一种特殊类型,它引用在函数本身之外声明的变量。 这也就是说我们在使用函数的时候不是将变量传递给函数,而是使用在函数之外声明的变量。
这与常规函数如何引用全局变量非常相似。 您不是直接将这些变量作为参数传递给函数,而是在调用函数时可以访问它们。
package main
import "fmt"
func main() {
n := 0
counter := func() int {
n += 1
return n
}
fmt.Println(counter())
fmt.Println(counter())
}
输出结果
1
2
上面的例子中并没有传递变量n到匿名函数但是它可以访问并使用这个变量,这样就形成了闭包的概念。
上面的例子有个问题就是在main函数中可以访问并更改变量n,为了隔离n使得其他代码不能访问它,我们要使用闭包的另一个特性就是:即使在别处不再引用,仍可以引用在创建过程中可以访问的变量。听起来有点难以理解举个例子
package main
import "fmt"
func adder() func(int) int {
sum := 0
return func(x int) int {
sum += x
fmt.Printf("x: %d, addr of x:%p\n", x, &x)
fmt.Printf("sum: %d, addr of sum:%p\n", sum, &sum)
return sum
}
}
func main() {
pos := adder()
for i := 0; i < 10; i++ {
fmt.Println(
pos(i),
)
}
}
输出结果
x: 0, addr of x:0xc0000b6018
sum: 0, addr of sum:0xc0000b6010
0
x: 1, addr of x:0xc0000b6028
sum: 1, addr of sum:0xc0000b6010
1
x: 2, addr of x:0xc0000b6040
sum: 3, addr of sum:0xc0000b6010
3
x: 3, addr of x:0xc0000b6048
sum: 6, addr of sum:0xc0000b6010
6
x: 4, addr of x:0xc0000b6050
sum: 10, addr of sum:0xc0000b6010
10
x: 5, addr of x:0xc0000b6058
sum: 15, addr of sum:0xc0000b6010
15
x: 6, addr of x:0xc0000b6060
sum: 21, addr of sum:0xc0000b6010
21
x: 7, addr of x:0xc0000b6068
sum: 28, addr of sum:0xc0000b6010
28
x: 8, addr of x:0xc0000b6070
sum: 36, addr of sum:0xc0000b6010
36
x: 9, addr of x:0xc0000b6078
sum: 45, addr of sum:0xc0000b6010
45
我们可以在每次adder() 运行中sum的地址是不变的,这也是闭包的特性。这就是在函数调用之间保持数据持久性,同时又将数据与其他代码隔离的方式。