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