跳到主要内容

Go 闭包

提示
  1. 闭包定义:Go 闭包是嵌套函数,允许在外部函数关闭后仍然访问其变量,例如 func greet() func() string { name := "John"; return func() string { name = "Hi " + name; return name }}
  2. 返回函数:闭包可以通过返回嵌套匿名函数来创建,如 func greet() func() { return func() { fmt.Println("Hi John") }},并通过分配给变量进行调用。
  3. 数据隔离:每次调用外部函数时,闭包会返回新的函数实例,这些实例彼此独立,确保数据隔离,如在 displayNumbers() 中返回的闭包独立于其他闭包。

Go 闭包是一个嵌套函数,它允许我们在外部函数关闭后仍然访问外部函数的变量。

在我们学习闭包之前,让我们先回顾以下概念:

  • 嵌套函数
  • 返回一个函数

Golang 中的嵌套函数

在 Go 中,我们可以在一个函数内部创建另一个函数。这被称为嵌套函数。例如,

package main
import "fmt"

// 外部函数
func greet(name string) {

// 内部函数
var displayName = func() {
fmt.Println("Hi", name)
}

// 调用内部函数
displayName()

}

func main() {

// 调用外部函数
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() 表明该函数返回另一个函数。

如果您查看此函数的返回语句,我们可以看到函数正在返回一个函数。

main() 中,我们调用 greet() 函数。

g1 := greet()

这里,返回的函数现在被分配给 g1 变量。因此,g1() 执行嵌套的匿名函数。

Go 闭包

正如我们已经讨论过的,闭包是一个嵌套函数,它帮助我们在外部函数关闭后仍然访问外部函数的变量。让我们看一个例子。

package main
import "fmt"

// 外部函数
func greet() func() string {

// 在内部函数外部定义的变量
name := "John"

// 返回一个嵌套的匿名函数
return func() string {
name = "Hi " + name
return name
}
}

func main() {

// 调用外部函数
message := greet()

// 调用内部函数
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

// 返回内部函数
return func() int {
num = num + 2
return num
}

}

func main() {

// 调用外部函数
odd := calculate()

// 调用内部函数
fmt.Println(odd())
fmt.Println(odd())
fmt.Println(odd())

// 再次调用外部函数
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

// 内部函数
return func() int {
number++
return number
}

}

func main() {

// 返回一个闭包
num1 := displayNumbers()

fmt.Println(num1())
fmt.Println(num1())
fmt.Println(num1())

// 返回一个新闭包
num2 := displayNumbers()
fmt.Println(num2())
fmt.Println(num2())

}

输出

1
2
3
1
2

在上面的例子中,displayNumbers() 函数返回一个匿名函数,该函数将数字增加 1

return func() int {
number++
return number
}

main() 函数内,我们将函数调用分配给 num1 和 num2 变量。

这里,我们首先使用 num1() 调用闭包函数。在这种情况下,我们得到的输出为 123

然后,我们使用 num2() 再次调用它。在这种情况下,number 变量的值不是从 3 开始,而是再次从 1 开始。

这表明从 displayNumbers() 返回的闭包彼此隔离。