在学Golang之前,我一直写的是Java,而在Java中,闭包我几乎没用过,虽然好像Java也有闭包。对于我这样的一个前Java程序员来说,理解闭包有点困难,因此我换一种思路,企图用面向对象的思维去理解闭包,在JS中,闭包甚至可以实现面向对象。当然这种理解方式可能有失偏颇,属于我的个人理解,仅供参考。本文将以Golang官网的斐波那契示例为例,足够经典,也容易理解。
什么是闭包
这是我们首先提出的一个问题,那么什么是闭包,这是摘自维基百科的定义:
在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是在支持头等函数是其入口地址)和一个关联的环境(相当于一个符号查找表)。环境里是若干对符号和值的对应关系,它既要包括约束变量数也可能没有自由变量。闭包跟函数最大的不同在于,当捕捉闭包的时候,它的自由变量会在捕捉时被确定,这样即便脱离了捕捉时的上下文,它也能照常运行。捕捉时对于值的处理可以是值拷贝,也可以是名称引用,这通常由语言设计者决定,也可能由用户自行指定(如C++)。
看了一头雾水是不是,我也一头雾水,下面再来看看golang官方对闭包的定义:
A closure is a function value that references variables from outside its body. The function may access and assign to the referenced variables; in this sense the function is "bound" to the variables.
讲得还算清晰,但是也是有点不太懂。
我对闭包的理解:函数里面有函数,就这么简单
如何理解闭包
下面用Golang的官方示例来理解一下,闭包是个啥
package main
import "fmt"
// fib returns a function that returns
// successive Fibonacci numbers.
func fib() func() int {
a, b := 0, 1
return func() int {
a, b = b, a+b
return a
}
}
func main() {
f := fib()
// Function calls are evaluated left-to-right.
fmt.Println(f(), f(), f(), f(), f())
}
正如示例里面看到的那样,fib()
函数里面,又包含了一个匿名函数,这样的结构就可以称之为闭包。
我在起初看这个示例的时候,有点懵,不懂为什么每次调用函数的时候a,b的值会改变,而不是从0 开始
换一种方式,可以这么理解,因为 f()
的生命周期还没有结束,所以 a,b
的值仍然在内存中,这个 f()
函数可以理解为一个对象。
换成面向对象的写法,就很容易理解了:
package main
type Fib struct {
a int
b int
}
func (f *Fib) Fib() int {
f.a, f.b = f.b, f.a+f.b
return f.a
}
func main() {
f := Fib{0, 1}
println(f.Fib(), f.Fib(), f.Fib(), f.Fib(), f.Fib())
}
在这两个示例中,函数 f()
的内部变量 a,b
,相当于下面对象 f
的成员变量 a,b
;函数 f()
内部的匿名函数,相当于对象 f
的方法 fib()
;而 f := fib()
这个操作,则相当于 f := Fib{0, 1}
,新建了一个对象,如此去理解,就能很好的理解闭包这个概念了。
所以闭包有什么用
按照某些说法,闭包的最初目的是为了减少全局变量,在函数调用过程中隐式地传递共享变量。然而这种操作的结果不够直接,所以一般不建议使用闭包。
最后提一嘴
这篇文章是我在理解闭包的时候有感而发的一篇文章,文中很多东西可能不是特别正确,甚至有失偏颇,本文仅供参考。