Go語言中的閉包詳解
一、函數(shù)的變量作用域和可見性
1.全局變量在main函數(shù)執(zhí)行之前初始化,全局可見
2.局部變量在函數(shù)內(nèi)部或者if、for等語句塊有效,使用之后外部不可見
3.全局變量和局部變量同名的情況下,局部變量生效。
4.可見性:
包內(nèi)任何變量或函數(shù)都是能訪問的。
包外的話,首字母大寫是可以訪問的,首字母小寫的表示私有的不能被外部調(diào)用。
二、匿名函數(shù)
1.Go語言中函數(shù)也是一種類型,所以可以用一個(gè)函數(shù)類型的變量進(jìn)行接收。
func anonyTest1(){ fmt.Println("anonyTest1") } //將改函數(shù)賦值給一個(gè)變量f,執(zhí)行f func AnonyTest(){ f:= anonyTest1 f() }
2.匿名函數(shù)就是不指定名稱的函數(shù),如下就是匿名函數(shù)的使用
func AnonyTest2(){ f:= func() { fmt.Println("AnonyTest2") } f() //或者 func() { fmt.Println("AnonyTest2...") }() }
3.下面一個(gè)例子結(jié)合defer來看一下,這三個(gè)輸出都是什么
func AnonyTest3(){ var i=0 defer func() { fmt.Printf("defer func i=%v \n",i) }() defer fmt.Printf("defer i=%v \n",i) for;i<10; i++{ } fmt.Printf("i=%v \n",i) }
從defer那篇文章我們知道 defer fmt.Printf("defer i=%v \n",i) 打印的就是i初始化后的值,最后一個(gè)也一定是for循環(huán)之后的值10,
主要就是匿名函數(shù)執(zhí)行之后的值,有意思是10,說明訪問了匿名函數(shù)外部的i,這就涉及到了閉包
運(yùn)行結(jié)果如下:
i=10
defer i=0
defer func i=10
4.既然函數(shù)也是一種類型,那么就可以把函數(shù)當(dāng)做參數(shù)進(jìn)行輸入、輸出了。(感覺有點(diǎn)類似C#里面的委托)
func Calc(a,b int, op func(int,int)int) int { return op(a,b) } func add(a,b int) int{ return a+b } func sub(a,b int)int{ return a-b } func AnonyTest4(){ var a = 2 var b = 1 var x = Calc(a,b,add) var y = Calc(a,b,sub) fmt.Printf("x=%v, y=%v \n",x,y) }
結(jié)果:
x=3, y=1
三、閉包
閉包是由函數(shù)和與其相關(guān)的引用環(huán)境組合而成的實(shí)體(好抽象,難理解?。?/p>
func Adder() func(int) int{ var x int return func(d int) int{ x+=d return x } }
像上面這段代碼,我們可以看到定義了一個(gè)變量x,以及return中的匿名函數(shù)。我們可以看到匿名函數(shù)引用了外部的變量x,我們可以把這個(gè)x叫做自由變量。
換句話說,這個(gè)匿名函數(shù)和這個(gè)自由變量x組成了一個(gè)整體,只要是在這個(gè)整體的生命周期內(nèi)這個(gè)x都是有效的。
下面使用一下這個(gè)Adder函數(shù):
func ClosureDemo5(){ var f = Adder() fmt.Printf("結(jié)果=%d\n",f(1)) fmt.Printf("結(jié)果=%d\n",f(20)) fmt.Printf("結(jié)果=%d\n",f(300)) }
執(zhí)行結(jié)果
結(jié)果=1
結(jié)果=21
結(jié)果=321
正如上面所提到的,這個(gè)只要Addr() 也就是f這個(gè)對(duì)象沒有消亡,那么f中的這個(gè)x就始終存在,也就是為什么第二次是21,第三次是321的原因了。
其他例子:
例子1:
func Adder2(base int) func(int)int{ return func(i int) int{ base += i return base } } func main(){ tmp1 := Adder2(10) fmt.Println(tmp1(1),tmp1(2)) tmp2 := Adder2(100) fmt.Println(tmp2(10),tmp2(20)) }
這里Adder2接收一個(gè)int類型參數(shù)base,然后返回一個(gè)func,這里這個(gè)匿名函數(shù)里面引用了這個(gè)參數(shù)base,那么這個(gè)參數(shù)base和匿名函數(shù)就形成了一個(gè)整體。
后面我們 tmp1被賦值為 Adder2(10) ,那么在tmp1這個(gè)對(duì)象的生命周期內(nèi),base是被初始化為10且一直存在,所以結(jié)果是 11 和 13,同理后面是 110 和 130
例子2:
func calc(base int) (func(int)int,func(int)int){ add:= func(i int)int{ base +=i return base } sub:= func(i int)int{ base -= i return base } return add,sub } func main(){ f1,f2 := calc(10) fmt.Println(f1(1),f2(2)) fmt.Println(f1(3),f2(4)) fmt.Println(f1(5),f2(6)) fmt.Println(f1(7),f2(8)) }
分析一下:
這里base和 add以及sub的匿名函數(shù)也組成了一個(gè)實(shí)體也就是calc,所以在f1和f2的生命周期內(nèi),base一直存在,并被初始化成了10.
所以結(jié)果就是 f1(1) 就是10+1 =11 而 f2(2)就是 11-2 = 9,其他同理。
所以結(jié)果如下:
11 9
12 8
13 7
14 6
閉包的副作用!
func main(){ for i:=0;i<5;i++{ go func(x int){ fmt.Println(x) }(i) } time.Sleep(time.Second) }
上述代碼應(yīng)該結(jié)果是多少?我的猜想應(yīng)該是0、1、2、3、4
但是實(shí)際結(jié)果是:
5
5
5
5
5
為什么會(huì)出現(xiàn)這樣的情況?實(shí)際上面這里每一個(gè)go協(xié)程中的匿名函數(shù)和外部for循環(huán)的i也形成了閉包,因?yàn)閒or循環(huán)執(zhí)行比較快,所以go還沒來得及執(zhí)行就變成5了。
我在每一個(gè)go協(xié)程之后加一個(gè)延時(shí),結(jié)果就是0,1,2,3,4了。
func main(){ for i:=0;i<5;i++{ go func(){ fmt.Println(i) }() time.Sleep(time.Second) } time.Sleep(time.Second) }
結(jié)果如下
0
1
2
3
4
問題就在于不可能每次執(zhí)行都進(jìn)行延遲吧,所以需要做一件事情打破這個(gè)閉包。
func main(){ for i:=0;i<5;i++{ go func(x int){ fmt.Println(x) }(i) } time.Sleep(time.Second) }
這里把i當(dāng)做參數(shù)傳入到匿名函數(shù)中,保證了每次循環(huán)傳的值都不一樣。
到此這篇關(guān)于Go語言閉包的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Go語言防范SQL注入CSRF及XSS攻擊實(shí)例探究
在本文中,我們將會(huì)介紹幾種最常見的攻擊類型,并且介紹如何使用Golang來防范這些攻擊,本文會(huì)涉及XSS攻擊、CSRF攻擊、SQL注入等,如果你想學(xué)習(xí)Golang和網(wǎng)絡(luò)安全的相關(guān)知識(shí),那么這篇文章會(huì)是一個(gè)很好的開始2024-01-01Air實(shí)現(xiàn)Go程序?qū)崟r(shí)熱重載使用過程解析示例
這篇文章主要為大家介紹了Air實(shí)現(xiàn)Go程序?qū)崟r(shí)熱重載使用過程解析示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2022-04-04Golang使用Gin框架實(shí)現(xiàn)http分塊傳輸
這篇文章主要為大家詳細(xì)介紹了Golang中如何使用Gin框架實(shí)現(xiàn)http分塊傳輸功能,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,需要的可以參考一下2023-05-05