Go 闭包是一个嵌套函数,它允许我们在外部函数关闭后仍然访问外部函数的变量。
在学习闭包之前,让我们先回顾一下以下概念
- 嵌套函数
- 返回函数
Golang 中的嵌套函数
在 Go 中,我们可以在另一个函数内部创建一个函数。这被称为嵌套函数。例如,
package main
import "fmt"
// outer function
func greet(name string) {
// inner function
var displayName = func() {
fmt.Println("Hi", name)
}
// call inner function
displayName()
}
func main() {
// call outer function
greet("John") // Hi John
}
在上面的示例中,我们在 greet()
函数内部创建了一个匿名函数。
这里,var displayName = func() {...}
是一个嵌套函数。嵌套函数的工作方式与普通函数类似。当在 greet()
函数内部调用 displayName()
时,它就会执行。
在 Go 中返回函数
我们可以创建一个返回匿名函数的函数。例如,
package main
import "fmt"
func greet() func() {
return func() {
fmt.Println("Hi John")
}
}
func main() {
g1 := greet()
g1()
}
输出
Hi John
在上面的示例中,我们创建了 greet()
函数。
func greet() func() {...}
这里,花括号之前的 func()
表示该函数返回另一个函数。
另外,如果你查看该函数的 return 语句,可以看到该函数返回了一个函数。
从 main()
中,我们调用 greet()
函数。
g1 := greet()
这里,返回的函数现在被赋给了 g1
变量。因此,g1()
执行嵌套的匿名函数。
Go 闭包
正如我们已经讨论过的,闭包是一个嵌套函数,它帮助我们在外部函数关闭后仍然访问外部函数的变量。让我们看一个例子。
package main
import "fmt"
// outer function
func greet() func() string {
// variable defined outside the inner function
name := "John"
// return a nested anonymous function
return func() string {
name = "Hi " + name
return name
}
}
func main() {
// call the outer function
message := greet()
// call the inner function
fmt.Println(message())
}
输出
Hi John
在上面的示例中,我们创建了一个名为 greet()
的函数,它返回一个嵌套的匿名函数。
这里,当我们从 main()
调用函数时。
message := greet()
返回的函数现在被赋值给 message 变量。
此时,外部函数的执行已完成,因此 name 变量应该被销毁。但是,当我们使用
fmt.Println(message())
调用匿名函数时,我们可以访问外部函数的 name 变量。
这是可能的,因为嵌套函数现在充当了一个闭包,它在外部函数执行后仍然将外部作用域变量封闭在其作用域内。
让我们看另一个例子来使这个概念更清楚。
示例:使用 Golang 闭包打印奇数
package main
import "fmt"
func calculate() func() int {
num := 1
// returns inner function
return func() int {
num = num + 2
return num
}
}
func main() {
// call the outer function
odd := calculate()
// call the inner function
fmt.Println(odd())
fmt.Println(odd())
fmt.Println(odd())
// call the outer function again
odd2 := calculate()
fmt.Println(odd2())
}
输出
3 5 7 3
在上面的例子中:
odd := calculate()
此代码执行外部函数 calculate()
并返回一个用于奇数的闭包。这就是为什么我们可以在外部函数完成后仍然访问 calculate()
的 num 变量。
再次,当我们使用
odd2 := calculate()
调用外部函数时,会返回一个新的闭包。因此,当我们调用 odd2()
时,我们再次得到 3。
闭包有助于数据隔离
正如我们在前面的例子中看到的,每次调用外部函数时都会返回一个新的闭包。这里,每个返回的闭包都是相互独立的,一个闭包的更改不会影响另一个。
这有助于我们处理多个数据,并且彼此隔离。让我们看一个例子。
package main
import "fmt"
func displayNumbers() func() int {
number := 0
// inner function
return func() int {
number++
return number
}
}
func main() {
// returns a closure
num1 := displayNumbers()
fmt.Println(num1())
fmt.Println(num1())
fmt.Println(num1())
// returns a new closure
num2 := displayNumbers()
fmt.Println(num2())
fmt.Println(num2())
}
输出
1 2 3 1 2
在上面的示例中,displayNumbers()
函数返回一个匿名函数,该函数将 number 增加 1。
return func() int {
number++
return number
}
在 main()
函数中,我们将函数调用赋值给 num1 和 num2 变量。
这里,我们首先通过 num1()
调用闭包函数。在这种情况下,我们得到输出 1、2 和 3。
再次,我们通过 num2()
调用它。在这种情况下,number 变量的值不是从 3 开始;而是再次从 1 开始。
这表明从 displayNumbers()
返回的闭包是相互隔离的。