关闭基础知识
一个封闭的使用环境一起的功能。该函数通常是在另一个函数内定义的匿名函数。环境是封闭函数的词法范围(函数词法范围的基本思想是函数括号之间存在的范围。)
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