现在为止,闭包一共有两种说法:
- 第一种说法认为闭包是符合一定条件的函数,其定义为:闭包是在其词法上下文中引用了自由变量的函数。
- 第二种说法认为闭包是由函数和与其相关的引用环境组合而成的实体,其定义为:在实现深约束时,需要创建一个能显示表达引用环境的东西,并将它与相关的子程序捆绑在一起,这样捆绑起来的整体被称为闭包。为 函数+引用环境=闭包。
显然第二种说法更确切,闭包只不过是在形式和表现上更像函数,实际上不是函数。
函数和闭包的区别是:
- 函数是一些可执行的代码,这些代码在函数被定义后就确定了,不会在执行时发生变化,所以一个函数只有一个实例。
- 闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。
闭包在某些编程语言中被称为Lambda表达式。
函数本身并不存储任何信息,只有与引用的环境结合后形成的闭包才具有“记忆性”。函数是编译器静态的概念,而闭包是运行期动态的概念。
对象是附有行为的数据,而闭包是附有数据的行为。
- 加强模块化
- 闭包有益于模块化编程,它能以简单的方式开发较小的模块,从而提高开发速度和程序的可复用性。和没有使用闭包的程序相比,使用闭包可以将模块划分的更小。
- 抽象
- 闭包是数据和行为的组合,这使得闭包具有较好的抽象能力。
- 简化代码
-
哪些特性可以支持闭包
- 函数是一阶值(First-class value,一等公民),即函数可以作为另一个函数的返回值或参数,还可以作为一个变量的值。
- 函数可以嵌套定义,即在一个函数内部可以定义另一个函数。
- 允许定义匿名函数。
- 可以捕获引用环境,并把引用环境和函数代码组成一个可调用的实体。
-
例子
//没有用闭包进行计数的代码
func main() {
for i:=0;i<10 ;i++ {
fmt.Printf("i=%d\t",i)
fmt.Println(add(i))
}
}
func add(x int) int {
sum := 0
sum += x
return sum
}
输出:
i=0 0
i=1 1
i=2 2
i=3 3
i=4 4
i=5 5
i=6 6
i=7 7
i=8 8
i=9 9
上面这个例子中,当每一次执行到add()函数中的时候,sum变量都会重新置为0,达不到计数的目的,如果非要用这种写法来实现计数,则需要使用闭包。
//使用闭包实现计数
func main() {
result := add()
for i:=0;i<10;i++ {
fmt.Printf("i=%d \t",i)
fmt.Printf("sum=%d \n",result(i))
}
}
//通过闭包来访问其中的变量
func add() func(int) int {
sum := 0
result := func(num int) int{
sum += num
return sum
}
return result
}
输出:
i=0 sum=0
i=1 sum=1
i=2 sum=3
i=3 sum=6
i=4 sum=10
i=5 sum=15
i=6 sum=21
i=7 sum=28
i=8 sum=36
i=9 sum=45
而这个例子中则使用了闭包这个概念,add()函数中的return result返回的只是匿名函数的地址,所以在
同一个实例中,add()中的sum的值是不会重新被赋值为0的,只有新创建一个add()实例时,这个新实例中的sum才会置为0,如下所示:
//使用闭包实现计数
func main() {
result := add()
for i:=0;i<5;i++ {
fmt.Printf("i=%d \t",i)
fmt.Printf("sumOld=%d \n",result(i))
}
result2 := add()
fmt.Println("下面是一个新实例")
for i:=5;i<10;i++ {
fmt.Printf("i=%d \t",i)
fmt.Printf("sumNew=%d \n",result2(i))
}
//这里重新调用一下result实例,发现返回的sumOld值还是接续上面的
fmt.Println("重新调用result实例")
fmt.Printf("i=10 \t")
fmt.Printf("sumOld=%d \n",result(10))
}
//通过闭包来访问其中的变量
func add() func(int) int {
sum := 0
result := func(num int) int{
sum += num
return sum
}
return result
}
输出:
i=0 sumOld=0
i=1 sumOld=1
i=2 sumOld=3
i=3 sumOld=6
i=4 sumOld=10
下面是一个新实例
i=5 sumNew=5
i=6 sumNew=11
i=7 sumNew=18
i=8 sumNew=26
i=9 sumNew=35
重新调用result实例
i=10 sumOld=20
闭包的另一种写法:
func main() {
result := func() (func() int) {
i := 10
//由于上面的返回值类型是个函数,所以下面要return一个函数
return func() int {
i++
// 由于返回的函数的返回值类型是个整型数字,所以要返回一个整型数字
return i
}
}() // 这里需要加个括号对匿名函数进行调用
fmt.Println(result())
}
输出:
11