匿名函數(shù)同樣被稱之為閉包(函數(shù)式語(yǔ)言的術(shù)語(yǔ)):它們被允許調(diào)用定義在其它環(huán)境下的變量荚藻。閉包可使得某個(gè)函數(shù)捕捉到一些外部狀態(tài),例如:函數(shù)被創(chuàng)建時(shí)的狀態(tài)。
另一種表示方式為:一個(gè)閉包繼承了 函數(shù)所聲明時(shí)的作用域灼狰。這種狀態(tài)(作用域內(nèi)的變量)都被共享到閉包的環(huán)境中袱瓮,因此這些變量可以在 閉包中被操作缤骨,直到被銷毀。閉包經(jīng)常被用作包裝函數(shù):它們會(huì)預(yù)先定義好 1 個(gè)或多個(gè)參數(shù)以用于包 裝尺借。另一個(gè)不錯(cuò)的應(yīng)用就是使用閉包來(lái)完成更加簡(jiǎn)潔的錯(cuò)誤檢查荷憋。
僅僅從形式上將閉包簡(jiǎn)單理解為匿名函數(shù)是不夠的,還需要理解閉包實(shí)質(zhì)上的含義褐望。
實(shí)質(zhì)上看,閉包是由函數(shù)及其相關(guān)引用環(huán)境組合而成的實(shí)體(即:閉包=函數(shù)+引用環(huán)境)串前。閉包在運(yùn)行時(shí) 可以有多個(gè)實(shí)例瘫里,不同的引用環(huán)境和相同的函數(shù)組合可以產(chǎn)生不同的實(shí)例。由閉包的實(shí)質(zhì)含義荡碾,我們可 以推論:閉包獲取捕獲變量相當(dāng)于引用傳遞谨读,而非值傳遞;對(duì)于閉包函數(shù)捕獲的常量和變量坛吁,無(wú)論閉包 何時(shí)何處被調(diào)用劳殖,閉包都可以使用這些常量和變量,而不用關(guān)心它們表面上的作用域拨脉。
引用環(huán)境驗(yàn)證
package main
import "fmt"
func addNumber(x int) func(int) {
fmt.Printf("x :%d addr of x:%p\n", x, &x)
return func(y int) {
k := x + y
x = k
y = k
fmt.Printf("x :%d addr of x:%p\n", x, &x)
fmt.Printf("y :%d addr of y:%p\n", y, &y)
}
}
func main() {
addNum := addNumber(5)
addNum(1)
addNum(1)
addNum(1)
fmt.Println("-------------------------------")
addNum2 := addNumber(5)
addNum2(1)
addNum2(1)
addNum2(2)
}
output:
x :5 addr of x:0xc00000a0c8
x :6 addr of x:0xc00000a0c8
y :6 addr of y:0xc00000a110
x :7 addr of x:0xc00000a0c8
y :7 addr of y:0xc00000a128
x :8 addr of x:0xc00000a0c8
y :8 addr of y:0xc00000a140
-------------------------------
x :5 addr of x:0xc00000a158
x :6 addr of x:0xc00000a158
y :6 addr of y:0xc00000a168
x :7 addr of x:0xc00000a158
y :7 addr of y:0xc00000a180
x :9 addr of x:0xc00000a158
y :9 addr of y:0xc00000a198
根據(jù)上面代碼結(jié)果哆姻,發(fā)現(xiàn)X在匿名函數(shù)聲明時(shí)傳入,是引用該參數(shù)玫膀。后續(xù)調(diào)用同一個(gè)匿名函數(shù)x地址不變矛缨,但值會(huì)改變。而y值作為匿名函數(shù)參數(shù)每次調(diào)用都傳值帖旨,地址在改變箕昭。
斐波那契數(shù)
import "fmt"
func test(a, b int) func() int {
return func() int {
a, b = b, a+b
return a
}
}
func main() {
var a, b int = 1, 1
c := test(a, b)
for i := 0; i < 10; i++ {
fmt.Println(c())
}
}
output:
1
2
3
5
8
13
21
34
55
89
使用閉包調(diào)試:
方法1:
當(dāng)您在分析和調(diào)試復(fù)雜的程序時(shí),無(wú)數(shù)個(gè)函數(shù)在不同的代碼文件中相互調(diào)用解阅,如果這時(shí)候能夠準(zhǔn)確地知 道哪個(gè)文件中的具體哪個(gè)函數(shù)正在執(zhí)行落竹,對(duì)于調(diào)試是十分有幫助的。您可以使用 runtime 或 log 包 中的特殊函數(shù)來(lái)實(shí)現(xiàn)這樣的功能货抄。包 runtime 中的函數(shù) Caller() 提供了相應(yīng)的信息述召,因此可以在 需要的時(shí)候?qū)崿F(xiàn)一個(gè) where() 閉包函數(shù)來(lái)打印函數(shù)執(zhí)行的位置:
package main
import (
"fmt"
"runtime"
)
func main() {
where := func() {
_, file, line, _ := runtime.Caller(1)
fmt.Printf("%s:%d\n", file, line)
}
where()
fmt.Println("---next---")
where()
}
output:
H:/awesomeProject/foo/go42.go:13
---next---
H:/awesomeProject/foo/go42.go:15
方法2:
使用log包的打印函數(shù)
package main
import (
"fmt"
"log"
)
func main() {
log.SetFlags(log.Llongfile | log.LstdFlags)
where := log.Print
where()
fmt.Println("---next---")
where()
}
output:
2020/10/12 21:46:44 H:/awesomeProject/foo/go42.go:11:
---next---
2020/10/12 21:46:44 H:/awesomeProject/foo/go42.go:13: