Go 封闭基础

示例

一个封闭的使用环境结合在一起的功能。该函数通常是在另一个函数内部定义的匿名函数。环境是包围函数的词法范围(函数的词法范围的非常基本的概念就是存在于函数括号之间的范围)。

func g() {
    i := 0
    f := func() { // 匿名功能
        fmt.Println("f called")
    }
}

在f另一个函数(如)中定义的匿名函数(如)的主体内g,变量存在于两者的范围内,f并且g可以访问。但是,正是此范围的范围g构成了闭包的环境部分(函数部分为f),因此,对g范围内的变量所做的更改保留了它们的值(即,在调用之间环境仍然存在f)。

考虑以下功能:

func NaturalNumbers() func() int {
    i := 0
    f:= func() int { // f是闭包的功能部分
        i++
        return i
    }
    return f
}

在上述定义中,NaturalNumbers具有内函数f,其NaturalNumbers返回。在内部f,正在访问i范围内定义的变量NaturalNumbers。

我们从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开始,每个对nexecute的调用i++。

i两次调用之间保留的值。即,作为封闭的一部分的环境持续存在。

NaturalNumbers再次调用将创建并返回一个新函数。这将在中初始化一个新i的NaturalNumbers。这意味着新返回的函数形成另一个闭包,该闭包具有与函数相同的部分(still 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++
        // 倍数的参数(这里是i)也形成
        // 环境的一部分,并被保留
        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