闭包:一个函数与其相关的引用环境组合而成的实体。
这个概念不免有点抽象,先来看一个例子。
package main
import (
"fmt"
)
func Add() func(int) int {
var x int
return func(d int) int {
x += d
return x
}
}
func main() {
var f1 = Add()
var f2 = Add()
fmt.Println(f1(1))
fmt.Println(f1(10))
fmt.Println(f2(1))
fmt.Println(f2(10))
}
这块代码的输出如下:
函数Add返回了一个函数,返回的这个函数就是一个闭包。我们给函数变量f1传入参数10时输出是11,这是因为调用f1的两次是同一个环境而f2又是另外一个环境。
有过c编程经验的人一定不认同,因为在c中局部变量x是在栈中分配的,函数add返回以后,对应的栈就失效了,Add返回的那个函数中变量x就引用一个失效的位置了。所以闭包的环境中引用的变量是在堆上分配的内存,而不是栈。
这种写法在Go语言中合法的,语言会自动地识别出这种情况并在堆上分配c的内存,而不是函数f的栈上。识别出变量需要在堆上分配,是由编译器的一种叫escape analyze的技术实现的。
因此呢,闭包=函数+引用环境。
再写一个例子,
package main
/* 判断一个字符串是否是以.png结尾或.jpg结尾,如果不是则向它的末尾加上结尾*/
import (
"fmt"
"strings"
)
func makeSuffixFunc(suffix string) func (string) string{
return func (name string) string{
if strings.HasSuffix(name,suffix) == false {
return name + suffix
}
return name
}
}
func main() {
f1 := makeSuffixFunc(".png")
fmt.Println(f1("test"))
fmt.Println(f1("pic"))
f2 := makeSuffixFunc(".jpg")
fmt.Println(f2("test"))
fmt.Println(f2("pic"))
}
可以看出,对f1来说suffix变量在初始化为.png之后,suffix变量就会一直是.png。同样f2也一样。这就有点像面向对象中类的成员变量初始化的味道。
希望这篇文章对大家理解go闭包有一定的帮助。
这两段代码的github链接:https://github.com/burgess001/go_exercise/tree/master/example1