官方的讲,闭包是指能够包含自由变量(未绑定到特定对象)变量的代码块;这些变量不是在这个代码块内活在任何全局上下文中定义的,而是在定义代码块的环境中定义的(局部变量), 当在这个代码块所在环境的外部调用该代码块时,代码块和它所引用的自由变量构成闭包。以下图所示。
![]()
从图1很容易看出,自由变量是局部变量,外部没法访问,但和它同属一个局部环境的代码块却能够访问,所以,咱们能够经过代码块间接地访问内部的自由变量。若是代码块理解起来有点儿抽象,咱们下面看一个具体的例子:
package main import "fmt" func A() func(){ // n是自由变量 var n int = 2019 // 匿名函数至关于图1中的“代码块” return func() { n += 1 fmt.Println(n) } } // main()在外部环境中 func main() { myFunc := A() // 外部环境调用代码块 myFunc() myFunc() }
在上面的代码中,函数A内部有一个局部变量n, 为了能在外部环境中访问n,咱们让函数A返回了一个匿名函数,这个匿名函数有对n的访问权限,咱们在main函数调用函数A返回的匿名函数,能够修改并打印出n的值,代码编译运行的结果以下:
2020 2021 Process finished with exit code 0
能够看出,这个A内部的匿名函数就是函数A暴露给外部访问其内部变量n的一个“接口”,经过这个接口,调用者能够实如今全局环境中访问局部变量。
为了更好地理解闭包,咱们再看一个例子。编程
package main import ( "fmt" "strings" ) // 处理文件名 func makeSuffix(suffix string) func (string) string { return func(name string) string { // 匿名函数绑定的局部变量是外部函数形参suffix if !strings.HasSuffix(name, suffix) { // 若是文件名没有指定的后缀名,则给它加上指定的后缀名 name += suffix } return name } } func main() { // 1.指定文件后缀名为.jpg f := makeSuffix(".jpg") // 2.建立文件名 fileName1 := "flower" fileName2 := "flower.jpg" // 3.调用f res1 := f(fileName1) res2 := f(fileName2) // 4.打印闭包调用处理后的结果 fmt.Println("res1=", res1) fmt.Println("res2=", res2) }
res1= flower.jpg res2= flower.jpg Process finished with exit code 0
(1) 一般是经过嵌套的匿名函数的形式实现的;
(2) 匿名函数内部引用了外部函数的参数或变量;
(3) 被引用的参数和变量的生命周期和外部调用者的生命周期相同。
请看下面的代码:
package main import ( "fmt" "sync" ) var wg sync.WaitGroup func main() { wg.Add(10); for i:=0; i<10; i++ { // 以匿名函数的形式开启goroutine go func() { fmt.Println(i) wg.Done() }() } wg.Wait() }
2 7 7 3 7 10 10 10 10 7 Process finished with exit code 0
package main import ( "fmt" "sync" ) var wg sync.WaitGroup func main() { wg.Add(10); for i:=0; i<10; i++ { // 以匿名函数的形式开启goroutine go func(num int) { fmt.Println(num) wg.Done() }(i) } wg.Wait() }
编译执行结果以下:
5 6 2 0 7 9 3 8 1 4 Process finished with exit code 0
能够发现,每一个goroutine打印的结果都不同,打印顺序随机是因为goroutine之间的并发执行形成的,咱们经过匿名函数传参的形式就解决了这种因为不经意间使用了闭包(本身没有发现)带来的错误,在go语言的并发编程中,这种状况比较多见,咱们应该格外注意。
我是lioney,年轻的后端攻城狮一枚,爱钻研,爱技术,爱分享。
我的笔记,整理不易,感谢阅读、点赞和收藏。
文章有任何问题欢迎你们指出,也欢迎你们一块儿交流后端各类问题!