go語言學習--go中閉包
阿新 • • 發佈:2019-01-13
Go語言支援匿名函式,即函式可以像普通變數一樣被傳遞或使用。
使用方法如下:
package main import ( "fmt" ) func main() { var v func(a int) int v = func(a int) int { return a * a } fmt.Println(v(6)) //兩種寫法 v1 := func(i int) int { return i * i } fmt.Println(v1(7)) }
GO語言的匿名函式就是閉包,以下是《GO語言程式設計》中對閉包的解釋
基本概念
閉包是可以包含自由(未繫結到特定物件)變數的程式碼塊,這些變數不在這個程式碼塊內或者
任何全域性上下文中定義,而是在定義程式碼塊的環境中定義。要執行的程式碼塊(由於自由變數包含
在程式碼塊中,所以這些自由變數以及它們引用的物件沒有被釋放)為自由變數提供繫結的計算環
境(作用域)。
閉包的價值
閉包的價值在於可以作為函式物件或者匿名函式,對於型別系統而言,這意味著不僅要表示
資料還要表示程式碼。支援閉包的多數語言都將函式作為第一級物件,就是說這些函式可以儲存到
變數中作為引數傳遞給其他函式,最重要的是能夠被函式動態建立和返回。
一個函式和與其相關的引用環境,組合而成的實體:
package main import "fmt" func main() { var f = Adder() fmt.Println(f(1), "-") fmt.Println(f(20), "-") fmt.Println(f(300), "-") } func Adder() func(int) int { var x int return func(delta int) int { x += delta return x } }
使用閉包的注意點
(1)由於閉包會使得函式中的變數都被儲存在記憶體中,記憶體消耗很大,所以不能濫用閉包,否則會造成網頁的效能問題,在IE中可能導致記憶體洩露。解決方法是,在退出函式之前,將不使用的區域性變數全部刪除。
(2)閉包會在父函式外部,改變父函式內部變數的值。所以,如果你把父函式當作物件(object)使用,把閉包當作它的公用方法(Public Method),把內部變數當作它的私有屬性(private value),這時一定要小心,不要隨便改變父函式內部變數的值。
閉包的注意點
由於閉包使的函式的變數儲存在內中,會出現值被替換的情況
package main import "fmt" func main() { var flist []func() for i := 0; i < 3; i++ { flist = append(flist, func() { fmt.Println(i) }) } for _, f := range flist { f() } }
我們可以看到結果全是3。為什麼呢?我們可以列印下迴圈中i的地址
package main import "fmt" func main() { var flist []func() for i := 0; i < 3; i++ { fmt.Println(&i) flist = append(flist, func() { fmt.Println(i) }) } for _, f := range flist { f() } }
發現執行的結果
0xc0420080a8 0xc0420080a8 0xc0420080a8 3 3 3
i指向的地址是一樣的,那麼就很明瞭了i的變數被儲存了,最後的迴圈把i的值變成了3
如何改進呢,我們只需要每次初始化一個i就可以了
package main import "fmt" func main() { var flist []func() for i := 0; i < 3; i++ { i := i //給i變數重新賦值, fmt.Println(i) flist = append(flist, func() { fmt.Println(i) }) } for _, f := range flist { f() } }