闭包
阅读《Go语音核心编程》一书的时候,看到闭包这个概念,觉得文中讲解的不是很容易理解。因此,想自己结合网上可以查找到的资料,对其进行一次梳理。
概念
这里先转述一下书中的概念:
- 定义:闭包是由函数及其相关引用环境组合而成的实体,一般通过在匿名函数中引用外部函数的局部变量或者全局变量构成
但是看完之后,并不是特别清晰。这里使用书中的例子说一下自己的理解:
package main
import "fmt"
func fa(a int) func(i int) int {
return func(i int) int {
fmt.Println(&a, a)
a = a + i
return a
}
}
func main() {
f := fa(1) //f 引用的外部的闭包环境包括本次函数调用的形参a 的值 1 ,out:0xc000096068 1
g := fa(1) //g 引用的外部的闭包环境包括本次函数调用的形参a 的值 1,out:0xc00000a0d0 1
// 此时 f,g 引用的闭包环境中的 a 的值并不是同一个,而是两次函数调用产生的副本
fmt.Println(f(1))//,out:0xc000096068 1,2
//多次调用 f 引用的是同一个副本 a,所以这里就会是 3
fmt.Println(f(1))//,out:0xc000096068 2,3
//g 的 a 的值仍然是 1
fmt.Println(g(1))//,out:0xc00000a0d0 1,2
fmt.Println(g(1))//,out:0xc00000a0d0 2,3
}
简单理解,其实就是在一个函数(fa)内部创建另外一个函数(return func(i int)),通过另一个函数访问这个函数的局部变量 a。
第一次调用fa,fa 函数执行完退出,其中的匿名函数存储了变量 a,所以 a 并没有被销毁。因此,后续调用 f 的时候,依旧可以访问并且修改 a。相对于 f 来说,其实就是讲函数内部和函数外部连接起来。
需要注意的是,如果闭包引用修改了全局变量,那么每次调用都会影响全局变量。(这个需要避免,而且闭包的最初目的,就是为了减少全局变量)
闭包的优缺点
- 优点:
- 延长了变量的生命周期(fa 函数销毁之后,依旧可以修改 a 变量)
- 延迟执行:在需要在函数执行结束后再执行某些操作时非常有用,比如资源清理、释放等。
- 回调函数
- 缺点:
- 资源泄露: 闭包中捕获的外部变量可能导致资源泄露。
- 性能损耗: 闭包的实现可能会导致一定的性能损耗。需要在堆上分配内存来保存捕获的变量,相比普通函数,闭包可能会有更多的内存分配和垃圾回收开销。
- 代码可读性: 使用过度复杂的闭包可能会降低代码的可读性和可维护性。