Go語言中閉包是引用了自由變量的函數,被引用的自由變量和函數一同存在,即使已經離開了自由變量的環境也不會被釋放或者刪除,在閉包中可以繼續使用這個自由變量,因此,簡單的說:
~~~
函數 + 引用環境 = 閉包
~~~
同一個函數與不同引用環境組合,可以形成不同的實例,如下圖所示。

個函數類型就像結構體一樣,可以被實例化,函數本身不存儲任何信息,只有與引用環境結合后形成的閉包才具有“記憶性”,函數是編譯期靜態的概念,而閉包是運行期動態的概念。
#### 其它編程語言中的閉包
閉包(Closure)在某些編程語言中也被稱為 Lambda 表達式。
閉包對環境中變量的引用過程也可以被稱為“捕獲”,在[C++](http://c.biancheng.net/cplus/)11 標準中,捕獲有兩種類型,分別是引用和復制,可以改變引用的原值叫做“引用捕獲”,捕獲的過程值被復制到閉包中使用叫做“復制捕獲”。
在 Lua 語言中,將被捕獲的變量起了一個名字叫做 Upvalue,因為捕獲過程總是對閉包上方定義過的自由變量進行引用。
閉包在各種語言中的實現也是不盡相同的,在 Lua 語言中,無論閉包還是函數都屬于 Prototype 概念,被捕獲的變量以 Upvalue 的形式引用到閉包中。
C++ 與[C#](http://c.biancheng.net/csharp/)中為閉包創建了一個類,而被捕獲的變量在編譯時放到類中的成員中,閉包在訪問被捕獲的變量時,實際上訪問的是閉包隱藏類的成員。
## 在閉包內部修改引用的變量
閉包對它作用域上部的變量可以進行修改,修改引用的變量會對變量進行實際修改,通過下面的例子來理解:
~~~
// 準備一個字符串str := "hello world"// 創建一個匿名函數foo := func() { // 匿名函數中訪問str str = "hello dude"}// 調用匿名函數foo()
~~~
代碼說明如下:
* 第 2 行,準備一個字符串用于修改。
* 第 5 行,創建一個匿名函數。
* 第 8 行,在匿名函數中并沒有定義 str,str 的定義在匿名函數之前,此時,str 就被引用到了匿名函數中形成了閉包。
* 第 12 行,執行閉包,此時 str 發生修改,變為 hello dude。
代碼輸出:
```
hello dude
```
## 示例:閉包的記憶效應
被捕獲到閉包中的變量讓閉包本身擁有了記憶效應,閉包中的邏輯可以修改閉包捕獲的變量,變量會跟隨閉包生命期一直存在,閉包本身就如同變量一樣擁有了記憶效應。
累加器的實現:
~~~
package main
import "fmt"
func main() {
// 創建一個累加器
accumulator := Accumulate(1)
// 累加1并打印
fmt.Println(accumulator())
fmt.Println(accumulator())
// 打印累加器的函數地址
fmt.Printf("%p\n", &accumulator)
// 創建一個累加器,初始值為1
accumulator2 := Accumulate(10)
// 累加1并打印
fmt.Println(accumulator2())
// 打印累加器的函數地址
fmt.Printf("%p\n", &accumulator2)
}
// Accumulate 提供一個值,每次調用函數會指定對值進行累加
func Accumulate(value int) func() int {
// 返回一個閉包
return func() int {
// 累加
value++
return value
}
}
~~~
## 示例:閉包實現生成器
閉包的記憶效應被用于實現類似于[設計模式](http://c.biancheng.net/design_pattern/)中工廠模式的生成器,下面的例子展示了創建一個玩家生成器的過程。
玩家生成器的實現:
~~~
package main
import "fmt"
func main() {
// 創建一個玩家生成器
generator := playerGen("high noon")
name, hp := generator()
fmt.Println(name, hp)
}
// 創建一個玩家生成器,輸入名稱,輸出生成器
func playerGen(name string) func() (string, int) {
// 血量一直都為159
hp := 150
return func() (string, int) {
return name, hp
}
}
~~~
- 1.Go語言前景
- 2.Go語言環境搭建
- 3.Go語言的基本語法
- 3.1變量
- 3.1.1變量聲明
- 3.1.2變量初始化
- 3.1.3多個變量同時賦值
- 3.1.4匿名變量
- 3.1.5變量的作用域
- 3.1.6整型
- 3.1.7浮點類型
- 3.1.8復數
- 3.1.9bool類型
- 3.1.10字符串
- 3.1.11字符類型
- 3.1.12類型轉換
- 3.2常量
- 3.1.1const關鍵字
- 3.2.2模擬枚舉
- 4.Go語言的流程控制
- 4.2循環結構
- 4.3鍵值循環
- 4.4switch語句
- 4.5goto語句
- 4.6break語句
- 4.7continue語句
- 5.Go語言的函數
- 5.1函數聲明
- 5.2函數變量
- 5.3函數類型實現接口
- 5.4閉包
- 5.5可變參數
- 5.6defer(延遲執行語句)
- 5.7處理運行時錯誤
- 5.8宕機(panic)
- 5.9宕機恢復(recover)
- 5.10Test功能測試函數
- 6.Go語言的內置容器
- 6.1數組
- 6.2切片
- 6.3map
- 6.4sync.Map
- 6.5list
- 6.6range
- 7.Go語言的結構體
- 8.Go語言的接口
- 9.Go語言的常用內置包
- 10.Go語言的并發
- 11.Go語言的文件I/O操作
- 12.Go語言的網絡編程
- 13.Go語言的反射
- 14.Go語言的數據庫編程
- 15.Go語言密碼學算法
- 16.Go語言的gin框架
- 17.Go語言的網絡爬蟲
- 18.Go語言的編譯和工具鏈