golang中的闭包
閉包
簡(jiǎn)介
作用:縮小變量作用域,減少對(duì)全局變量的污染
閉包又是什么?你可以想象一下,在一個(gè)函數(shù)中存在對(duì)外來(lái)標(biāo)識(shí)符的引用。所謂的外來(lái)標(biāo)識(shí)符,既不代表當(dāng)前函數(shù)的任何參數(shù)或結(jié)果,也不是函數(shù)內(nèi)部聲明的,它是直接從外邊拿過(guò)來(lái)的
?
一個(gè)函數(shù)捕獲了和他在同一個(gè)作用域的其他常量和變量.這就意味著當(dāng)閉包被調(diào)用的時(shí)候,不管在程序什么地方調(diào)用,閉包能夠使用這些常量或者變量.
它不關(guān)心這些捕獲了的變量和常量是否已經(jīng)超出了作用域,所以只有閉包還在使用他,這些變量就還會(huì)存在.
在go里面,所有的匿名函數(shù)都是閉包
func main() {a := 10str := "mike"//匿名函數(shù),沒有函數(shù)名字,函數(shù)定義沒有調(diào)用f1 := func() {fmt.Println("a = ", a)fmt.Println("str = ", str)}//調(diào)用f1()//給一個(gè)函數(shù)類型起別名type FuncType func() //函數(shù)沒有參數(shù)沒有返回值//聲明變量var f2 FuncTypef2 = f1f2()//定義匿名函數(shù),同時(shí)調(diào)用func() {fmt.Printf("a = %d, str = %s\n", a, str)}() //后面的()代表調(diào)用此匿名函數(shù)//帶參數(shù)的匿名函數(shù)f3 := func(i, j int) {fmt.Printf("a = %d, str = %s\n", a, str)}f3(1, 2)//有參數(shù)有返回值x, y := func(i, j int) (max, min int) {if i > j {max = imin = j} else {max = jmin = i}return}(10, 20)fmt.Print(x, y) }閉包以引用的方式捕獲外部變量
func main() {a := 10str := "nike"func() {a = 666str = "go"fmt.Printf("內(nèi)部: a = %d, str = %s\n", a, str)}() //()代表直接調(diào)用fmt.Printf("外部: a = %d, str =%s\n", a, str) }輸出
內(nèi)部: a = 666, str = go 外部: a = 666, str =go閉包保存變量
func test02() func() int {var x int //沒有初始化,值為0return func() int {x++return x * x} }func main() {//返回函數(shù)類型f := test02()fmt.Println(f())fmt.Println(f())fmt.Println(f()) }使用
Go 函數(shù)可以是一個(gè)閉包。閉包是一個(gè)函數(shù)值,它引用了函數(shù)體之外的變量。 這個(gè)函數(shù)可以對(duì)這個(gè)引用的變量進(jìn)行訪問和賦值;換句話說(shuō)這個(gè)函數(shù)被“綁定”在這個(gè)變量上。
例如,函數(shù) adder 返回一個(gè)閉包。每個(gè)返回的閉包都被綁定到其各自的 sum 變量上。
在上面例子中(這里重新貼下代碼,和上面代碼一樣):
package mainimport "fmt"func adder() func(int) int {sum := 0return func(x int) int {sum += xreturn sum} }func main() {pos, neg := adder(), adder()for i := 0; i < 10; i++ {fmt.Println(pos(i),neg(-2*i),)} }如pos := adder()的adder()表示返回了一個(gè)閉包,并賦值給了pos,同時(shí),這個(gè)被賦值給了pos的閉包函數(shù)被綁定在sum變量上,因此pos閉包函數(shù)里的變量sum和neg變量里的sum毫無(wú)關(guān)系。
func adder() func(int) int的func(int) int表示adder()的輸出值的類型是func(int) int這樣一個(gè)函數(shù)
沒有閉包的時(shí)候,函數(shù)就是一次性買賣,函數(shù)執(zhí)行完畢后就無(wú)法再更改函數(shù)中變量的值(應(yīng)該是內(nèi)存釋放了);有了閉包后函數(shù)就成為了一個(gè)變量的值,只要變量沒被釋放,函數(shù)就會(huì)一直處于存活并獨(dú)享的狀態(tài),因此可以后期更改函數(shù)中變量的值(因?yàn)檫@樣就不會(huì)被go給回收內(nèi)存了,會(huì)一直緩存在那里)。
比如,實(shí)現(xiàn)一個(gè)計(jì)算功能:一個(gè)數(shù)從0開始,每次加上自己的值和當(dāng)前循環(huán)次數(shù)(當(dāng)前第幾次,循環(huán)從0開始,到9,共10次),然后*2,這樣迭代10次:
沒有閉包的時(shí)候這么寫:
func abc(x int) int {return x * 2 }func main() {var a intfor i := 0; i < 10; i ++ {a = abc(a+i)fmt.Println(a)} }如果用閉包可以這么寫:
func abc() func(int) int {res := 0return func(x int) int {res = (res + x) * 2return res} }func main() {a := abc()for i := 0; i < 10; i++ {fmt.Println(a(i))} }2種寫法輸出值都是:
0 2 8 22 52 114 240 494 1004 2026從上面例子可以看出閉包的3個(gè)好處:
不是一次性消費(fèi),被引用聲明后可以重復(fù)調(diào)用,同時(shí)變量又只限定在函數(shù)里,同時(shí)每次調(diào)用不是從初始值開始(函數(shù)里長(zhǎng)期存儲(chǔ)變量)
這有點(diǎn)像使用面向?qū)ο蟮母杏X,實(shí)例化一個(gè)類,這樣這個(gè)類里的所有方法、屬性都是為某個(gè)人私有獨(dú)享的。但比面向?qū)ο蟾拥妮p量化
用了閉包后,主函數(shù)就變得簡(jiǎn)單了,把算法封裝在一個(gè)函數(shù)里,使得主函數(shù)省略了a=abc(a+i)這種麻煩事了
變量污染少,因?yàn)槿绻麤]用閉包,就會(huì)為了傳遞值到函數(shù)里,而在函數(shù)外部聲明變量,但這樣聲明的變量又會(huì)被下面的其他函數(shù)或代碼誤改。
關(guān)于閉包的第一個(gè)好處,再啰嗦舉個(gè)例子
輸出:
2 1如果手誤將A := 2寫成了A = 2,那么輸出就是:
2 2即會(huì)影響外部變量A
輸出:
2 3在bar里是可以對(duì)變量A做操作的,一個(gè)不小心就容易誤修改變量A
**結(jié)論:函數(shù)外的變量只能通過(guò)參數(shù)傳遞進(jìn)去,不要通過(guò)全局變量的方式的渠道傳遞進(jìn)去,當(dāng)函數(shù)內(nèi)能讀取到的變量越多,出錯(cuò)概率(誤操總結(jié)
以上是生活随笔為你收集整理的golang中的闭包的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: golang中的mysql类型对应
- 下一篇: golang工作区