關閉基礎知識

一個封閉的使用環境一起的功能。該函式通常是在另一個函式內定義的匿名函式。環境是封閉函式的詞法範圍(函式詞法範圍的基本思想是函式括號之間存在的範圍。)

func g() {
    i := 0
    f := func() { // anonymous function
        fmt.Println("f called")
    }
}

在另一個函式(比如 g)中定義的匿名函式(比如 f)的主體內,可以訪問 fg 範圍內的變數。然而,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 時,它都會增加 ii 從 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

no 都是包含相同功能部分的閉包(它們具有相同的行為),但環境不同。因此,閉包的使用允許函式訪問可用於在呼叫之間保留資訊的持久環境。

另一個例子:

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