官方的講,閉包是指能夠包含自由變量(未綁定到特定對象)變量的代碼塊;這些變量不是在這個代碼塊內活在任何全局上下文中定義的,而是在定義代碼塊的環境中定義的(局部變量), 當在這個代碼塊所在環境的外部調用該代碼塊時,代碼塊和它所引用的自由變量構成閉包。以下圖所示。
從圖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,年輕的後端攻城獅一枚,愛鑽研,愛技術,愛分享。
我的筆記,整理不易,感謝閱讀、點贊和收藏。
文章有任何問題歡迎你們指出,也歡迎你們一塊兒交流後端各類問題!