匿名函數是指不需要定義函數名的一種函數實現方式,它並不是一個新概念,最早可以回溯到1958年的 Lisp 語言。但是由於各種原因,C 和 C++ 一直都沒有對匿名函數給以支持,其他的各種語言,比如 JavaScript、C# 和 Objective-C 等語言都提供了匿名函數特性,當然也包含 Go 語言。
匿名函數
在 Go 裏面,函數可以像普通變量一樣被傳遞或使用,屬於和變量一樣的一級公民
,這與 C 語言的回調函數比較類似。不同的是,Go 語言支持隨時在代碼裏定義匿名函數。
匿名函數由一個不帶函數名的函數聲明和函數體組成,如下所示:
func(a, b int, z float64) bool {
return a*b <int(z)
}
匿名函數可以直接賦值給一個變量或者直接執行:
f := func(x, y int) int {
return x + y
}
func(ch chan int) {
ch <- ACK
} (reply_chan) // 花括號後直接跟參數列表表示函數調用
閉包
Go 的匿名函數是一個閉包,下面我們先來了解一下閉包的概念、價值和應用場景。
- 基本概念
閉包是可以包含自由(未綁定到特定對象)變量的代碼塊,這些變量不在這個代碼塊內或者任何全局上下文中定義,而是在定義代碼塊的環境中定義。要執行的代碼塊(由於自由變量包含在代碼塊中,所以這些自由變量以及它們引用的對象沒有被釋放)爲自由變量提供綁定的計算環境(作用域)。
- 閉包的價值
閉包的價值在於可以作爲函數對象或者匿名函數,對於類型系統而言,這意味着不僅要表示數據還要表示代碼。支持閉包的多數語言都將函數作爲第一級對象
,就是說這些函數可以存儲到變量中作爲參數傳遞給其他函數,最重要的是能夠被函數動態創建和返回。
- Go 語言中的閉包
Go 語言中的閉包同樣也會引用到函數外的變量。閉包的實現確保只要閉包還被使用,那麼被閉包引用的變量會一直存在,如下面代碼所示。
package main
import (
"fmt"
)
func main() {
var j int = 5
a := func()(func()) {
var i int = 10
return func() {
fmt.Printf("i, j: %d, %d\n", i, j)
}
}()
a()
j *= 2
a()
}
上述例子的執行結果是:
i, j: 10, 5
i, j: 10, 10
在上面的例子中,變量 a 指向的閉包函數引用了局部變量 i 和 j,i 的值被隔離,在閉包外不能被修改,改變 j 的值以後,再次調用 a,發現結果是修改過的值。
在變量 a 指向的閉包函數中,只有內部的匿名函數才能訪問變量 i,而無法通過其他途徑訪問到,因此保證了 i 的安全性。