闭包的概念是有点抽象的,光从概念上去理解,有点晦涩。
闭包=函数+引用环境;只看这句话是不是有点懵逼,别慌,先看一段闭包的真实代码:
func main() {
a := f1()
fmt.Println(a()) //输出1
fmt.Println(a()) //输出2
b := f1()
fmt.Println(b()) //输出1
}
func f1() func() int {
counter := 0
f2 := func() int {
counter++
return counter
}
return f2
}


上面这个函数f1就是一个闭包函数,闭包就是一个函数可以访问另一个函数的局部变量,这里f2函数可以访问f1函数的局部变量counter,所以形成了闭包,f1就是闭包函数,而main方法a、b两个变量,虽然都等于f1(),但是引用的环境发生了改变,counter并不是同一个counter,这里是不是发现a、b有点类似f1()的一个对象实例?当然,通俗理解也可也这么去看待,所以,这里的闭包=函数(f1)+引用环境(counter);
闭包有什么效果?
上面了解了闭包的基本概念,从上面的代码我们也看到,两次a()输出结果不一样,一次1一次2。道理讲,因为counter是f1函数的局部变量,所以上面两次输出都应该是1,但是发现第二次输出了2,为什么?原来,我们使用Go的内存逃逸分析,发现counter变量也发生了内存逃逸,局部变量本应该分配到栈上,但是逃逸到堆上了,相当于两次函数都引用了同一个变量counter,就算再执行一段fmt.Println(a())也不会输出1,会继续累加变成3,而b变量由于是新的引用环境,可以假设看作是函数的一个对象,和a里面的counter不会有任何关系。
闭包有什么作用?
从上面的例子可以看出:
1、闭包可以访问函数内部的局部变量
2、闭包由于每个实例引用的变量都是同一个地址,所以闭包函数里面的局部变量,可以当成全局变量来进行使用,达到全局变量的效果,并且每个实例对应的局部变量就是一个新的全局变量,把需要“记忆功能”的变量都封装到了函数中去。
当然,闭包也不能滥用,因为闭包的变量被分配到了堆中,会等待GC来进行回收,使用不当,可能会导致内存泄漏。