上QQ阅读APP看书,第一时间看更新
4.1.2 闭包
匿名函数可以赋值给一个变量,也可以直接写在另一个函数的内部,来看下面这段示例代码:
book/ch04/4.1/clo/main.go
1. package main
2.
3. import "fmt"
4.
5. func main() {
6. f := double()
7. fmt.Println(f())
8. fmt.Println(f())
9. fmt.Println(f())
10. fmt.Println(f())
11. }
12.
13. func double() func() int {
14. var r int
15. return func() int {
16. r++
17. return r*2
18. }
19. }
20.
21. //以下是程序运行结果
22. 2
23. 4
24. 6
25. 8
第13行至第19行,定义了一个函数。这个函数里面先声明了一个变量r,因为没有显式地赋值,所以其作为int类型的默认初始值是0。然后在double函数里又定义了一个匿名函数,这里是直接把这个函数“return”回去的。在匿名函数里,先针对r进行自加操作,然后再返回r*2,结果是2。请注意double函数的返回类型定义,思考当返回值是一个函数时该如何定义。
第6行,变量f接收了double函数的返回值,也就是那个匿名函数。
第7行至第10行表示打印f的结果,第22行至第25行是输出结果,即2、4、6、8。按照开始的分析,结果不应该是2、2、2、2吗?这说明匿名函数掌握了函数double定义的变量r,虽然r的定义在匿名函数的外面,但是匿名函数在编译的时候还是把这个变量包装进自己的函数内了,从而跨过了作用域的限制,可以让r一直存在,这就是闭包,这个匿名函数就是闭包函数。
说明
在面向函数的编程中,大的功能往往会通过函数切分成多个独立的较小的功能,而且不可以定义太多全局变量,这就导致变量的作用域往往是包。打个比方,闭包在作用上类似于面向对象编程中类的实例,它会把函数和所访问的变量打包到一起,不再关心这个变量原来的作用域,闭包本身可以看作是独立对象。闭包函数与普通函数的最大区别就是参数不是值传递,而是引用传递,所以闭包函数可以操作自己函数以外的变量。结合前面介绍的垃圾回收机制来看,因为闭包函数对外部变量的操作才使其不能被释放回收,从而跨过了作用域的限制。