关闭基础知识

一个封闭的使用环境一起的功能。该函数通常是在另一个函数内定义的匿名函数。环境是封闭函数的词法范围(函数词法范围的基本思想是函数括号之间存在的范围。)

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