闭包
概念
闭包函数:声明在一个函数中的函数,叫做闭包函数。
闭包:内部函数总是可以访问其所在的外部函数中声明的参数和变量,即使在其外部函数被返回(寿命终结)了之后。
特点
让外部访问函数内部变量成为可能;
局部变量会常驻在内存中;
可以避免使用全局变量,防止全局变量污染;
会造成内存泄漏(有一块内存空间被长期占用,而不被释放)
闭包的创建
闭包就是可以创建一个独立的环境,每个闭包里面的环境都是独立的,互不干扰。闭包会发生内存泄漏,**每次外部函数执行的时候,外部函数的引用地址不同,都会重新创建一个新的地址。**但凡是当前活动对象中又被内部子集引用的数据,那么这个时候,这个数据不删除,保留一根
闭包实例
示例一
- 返回一个内部函数
package main
import "fmt"
//函数片段
func add(base int) func(int) int { // 1:创建一个新内存, 其地址并命名为base用来存储int变量, 然后取出a内存处存放的值,复制一份,放入base内存
fmt.Printf("%p\n", &base) //2:打印变量地址0xc0420080e0, 里面存储着10【它是函数拷贝传值】
f := func(i int) int { //3:定义一个函数,并且用f指向它[f里面存储着这个函数的地址]
//7、将1用i存储起来
fmt.Printf("%p\n", &base) // 2:打印变量地址0xc0420080e0
base += i // 取出base里面存储的值 + i里面存储的值,然后将结果放入base存储起来
return base //将base里面存储的值返回
}
return f // 4:将函数地址f返回
}
func main() {
aa := 10
fmt.Printf("a的地址:%p\n", &aa) //0xc0420080a8
t1 := add(aa) //5、将add(a)返回的函数地址用一个t1存储起来
c := t1(1) //6、调用t1指向的函数,传参为1[传值],然后将返回结果用新建的内存c存储起来
fmt.Println(c, &c) //11 0xc0420540b0
fmt.Println(t1(2)) // 13 t1还是指向原来开辟的空间,此时base=11, 因此11+2
t2 := add(100) //重新开辟一个空间调用add(100),然后将返回的函数地址用一个t2存储起来,此时base为新建立的
fmt.Println(t2(1), t2(2))
}
- 返回多个内部函数
package main
import "fmt"
//返回加减函数,重点:内部函数时对外部变量的引用
func calc(base int) (func(int) int, func(int) int) {
fmt.Printf("%p\n", &base)
add := func(i int) int {
fmt.Printf("%p\n", &base)
base += i
return base
}
sub := func(i int) int {
fmt.Printf("%p\n", &base)
base -= i
return base
}
return add, sub
}
//由 main 函数作为程序入口点启动
func main() {
f1, f2 := calc(11111)
fmt.Println(f1(1), f2(1)) //执行顺序:f1 f2 println
}
示例二
涉及 goroutine 时的情况,程序片段如下所示:
package main
import (
"fmt"
"time"
)
//由 main 函数作为程序入口点启动
func main() {
for i:=0; i<5; i++ {
go func(){
fmt.Println(i) //i变量值也是引用.创建5个线程执行函数, for循环执行过程中可能执行完的时候,线程刚好处于i的某个值。
}()
}
time.Sleep(time.Second * 1)
}
每次程序的运行结果都是不一样的
代码改进:
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan int, 1)
for i:=0; i<5; i++ {
go func(){
ch <- 1
fmt.Println(i)
}()
<- ch
}
time.Sleep(time.Second * 1)
}
会阻塞等待:0-4值一定有
参考:https://blog.csdn.net/li_101357/article/details/80196650
实例三:
package main
import "fmt"
func funA() func(){
var a = 10;
return func (){
fmt.Println(a); // 打印处a存储的值
}
}
func main() {
var b = funA() //funA()返回一个函数地址,执行一个函数,并用b保存这个地址,此时b指向这个函数
b() // 调用b指向的函数
}
示例四:全局变量VS闭包函数
package main
import "fmt"
func Outer() func(){
var i = 0;
return func() {
i++;
fmt.Println(i)
}
}
func main(){
var c = Outer()
c()
c()
c()
var d = Outer()
d()
d()
d()
}
// 1 2 3 1 2 3
- 全局函数
package main
import "fmt"
var i = 0;
func Outer() func(){
return func() {
i++;
fmt.Println(i)
}
}
func main(){
var c = Outer()
c()
c()
c()
var d = Outer()
d()
d()
d()
}
// 1 2 3 4 5 6
实例六
package main
import "fmt"
func Outer() func() int{
var i = 0;
return func() int{
i++;
return i;
}
}
func main(){
c := Outer() // 返回一个函数
fmt.Println(c()) //1 调用内部函数,并且拷贝i的值,返回
fmt.Println(c()) //2 调用内部函数,并且拷贝i的值,返回
d := c // c里面存储着函数地址,c将存储的函数地址放入d中,此时d和c指向同一个函数
fmt.Println(d()) //3 调用内部函数,并且拷贝i的值,返回
fmt.Println(d()) //4 调用内部函数,并且拷贝i的值,返回
e := Outer() // 新开辟一个内存,用来调用函数。 e存储着这个内存的地址
fmt.Println(e()) //1 调用内部函数,并且拷贝i的值,返回
fmt.Println(e()) //2 调用内部函数,并且拷贝i的值,返回
fmt.Println(Outer()) //0x496a30
fmt.Println(Outer()()) //1
}