Go 语言中的闭包

本文主要介绍 Go 语言中的闭包。简单的说 Go 语言的闭包就是一个引用了外部自由变量匿名函数,被引用的自由变量和该匿名函数共同存在,不会因为离开了外部环境就被释放或者删除,还可以在这个匿名函数中继续使用。

1. Go 语言的匿名函数

在上文中我们了解到了一个新的词汇——匿名函数,我们先来学习一下Go语言中的匿名函数,再来了解在 Go 语言中如何使用闭包。匿名函数,顾名思义,就是隐藏函数名的函数。

代码示例

package main

import (
    "fmt"
)

var f = func() {
    fmt.Println("匿名函数作为变量来使用")
}

func main() {
    f()

    func() {
        fmt.Println("匿名函数直接使用")
    }()
}
  • 第7~9行:定义一个函数类型,值为一个匿名函数的变量;
  • 第 12 行:使用这个匿名函数;
  • 第 14~16 行:定义一个匿名函数。在这个函数后加上(),就可以直接使用这个匿名函数。

执行结果

图片描述

2. 匿名函数引用外部变量

如果在匿名函数内,使用了外部环境的变量,就构成了一个闭包。简单来讲就是一个函数内,使用匿名函数来操作函数内声明的变量。

代码示例

package main

import (
    "fmt"
)

func main() {
    str := "Hello World!"
    func() {
        str = "Hello Codey!"
    }()
    fmt.Println(str)
}
  • 第 10 行:匿名函数直接操作了main函数之中的变量str,将其修改为了"Hello Codey!";
  • 第 12 行:输出变量的值。

执行结果

图片描述

上述例子简单的构造了一个闭包,在匿名函数中并没有声明或者定义str这个变量,但是可以直接操作,就是引用可main函数中的自由变量。这个例子可能对自由变量的引用表现不是很直观,我们接下来使用defer和闭包相结合,深入了解一下闭包中的引用外部变量。

代码示例

package main

import (
    "fmt"
)

func main() {
    str := "Hello World!"
    defer func() {
        fmt.Println("defer str=", str)
    }()
    str = "Hello Codey!"
    fmt.Println("main str=", str)
}

执行结果

图片描述

从执行结果上来看应该是蛮出人意料的,因为前文介绍 defer 的时候明确介绍了 defer 后变量是保留它在 defer 时的值,而不会被 defer 之后的代码所改变。但是在闭包这边这个看起来不太适用,其实是适用的,只是闭包是引用了这个变量,也就是说,在 defer 时被保留下来的是这个变量的地址,后续代码改变的不是地址,而是这个地址存储的值,所以后续代码对这个变量的操作,都会反应到这个 defer 中。

Tips:关于变量的地址,在后续的Go语言的指针中会有详细的介绍。

3. 小结

本文主要介绍了 Go 语言中闭包的使用,需要注意以下几点:

  • 闭包就是匿名函数引用外部变量
  • 闭包中引用的变量会被外部环境改变,同时闭包内对变量的改变也会影响到外部环境的使