關閉基礎知識
一個封閉的使用環境一起的功能。該函式通常是在另一個函式內定義的匿名函式。環境是封閉函式的詞法範圍(函式詞法範圍的基本思想是函式括號之間存在的範圍。)
func g() {
i := 0
f := func() { // anonymous function
fmt.Println("f called")
}
}
在另一個函式(比如 g
)中定義的匿名函式(比如 f
)的主體內,可以訪問 f
和 g
範圍內的變數。然而,g
的範圍形成了閉包的環境部分(函式部分是 f
),因此,對 g
範圍內的變數所做的更改保留了它們的值(即,在呼叫 f
之間環境仍然存在)。
考慮以下功能:
func NaturalNumbers() func() int {
i := 0
f:= func() int { // f is the function part of closure
i++
return i
}
return f
}
在上面的定義中,NaturalNumbers
有一個 NaturalNumbers
返回的內部函式 f
。在 f
內部,正在訪問 NaturalNumbers
範圍內定義的變數 i
。
我們從 NaturalNumbers
得到一個新功能,如下所示:
n := NaturalNumbers()
現在 n
是一個關閉。它是一個函式(由 f
定義),它也有一個相關的環境(NaturalNumbers
的範圍)。
在 n
的情況下,環境部分僅包含一個變數:i
由於 n
是一個函式,它可以被稱為:
fmt.Println(n()) // 1
fmt.Println(n()) // 2
fmt.Println(n()) // 3
從上面的輸出可以看出,每次呼叫 n
時,它都會增加 i
。i
從 0 開始,每次呼叫 n
執行 i++
。
i
的值在呼叫之間保留。也就是說,作為關閉的一部分的環境仍然存在。
再次呼叫 NaturalNumbers
將建立並返回一個新函式。這將在 NaturalNumbers
內初始化一個新的 i
。這意味著新返回的函式形成另一個具有相同功能部分的閉包(仍然是 f
),但是一個全新的環境(一個新初始化的 i
)。
o := NaturalNumbers()
fmt.Println(n()) // 4
fmt.Println(o()) // 1
fmt.Println(o()) // 2
fmt.Println(n()) // 5
n
和 o
都是包含相同功能部分的閉包(它們具有相同的行為),但環境不同。因此,閉包的使用允許函式訪問可用於在呼叫之間保留資訊的持久環境。
另一個例子:
func multiples(i int) func() int {
var x int = 0
return func() int {
x++
// paramenter to multiples (here it is i) also forms
// a part of the environment, and is retained
return x * i
}
}
two := multiples(2)
fmt.Println(two(), two(), two()) // 2 4 6
fortyTwo := multiples(42)
fmt.Println(fortyTwo(), fortyTwo(), fortyTwo()) // 42 84 126