备注:本文大量的引用http://c.biancheng.net/view/59.html
非原创,写的原因是为了方便自己总结一下使用。
一、介绍
维基百科讲,闭包(Closure),是引用了自由变量的函数。
go语言中,闭包是引用了自由变量的匿名函数,被引用的自由变量和匿名函数一同存在,即使已经离开了自由变量的环境也不会被释放或者删除,在闭包中可以继续使用这个自由变量,因此,简单的说:
匿名函数 + 自由变量与引用环境 = 闭包
同一个匿名函数与不同引用环境组合,可以形成不同的实例,如下图所示。
图:闭包与函数引用
一个函数类型就像结构体一样,可以被实例化,函数本身不存储任何信息,只有与引用环境结合后形成的闭包才具有”记忆性“,函数是编译期静态的概念,而闭包是运行期动态的概念。
二、闭包的作用域
闭包对它作用域上部的变量可以进行修改,修改引用的变量会进行实际修改。
下面我们准备了一个闭包,组成有 自由变量str
以及匿名函数foo
。
// 准备一个字符串
str := "hello world"
// 创建一个匿名函数
foo := func() {
// 匿名函数中访问str
str = "hello hihi"
}
// 调用匿名函数
foo()
输出位 hello hihi
原因是 str的 作用域范围为这里的整个代码块。
foo := func() {
// 匿名函数中访问str
str = "hello hihi"
}
这里面的str,为这个外面的代码块的str,所以顺序执行,会修改str的值。
三、闭包的情况下,如果在同一个作用域,后面的值变了,goroutine里面的只有变量值也会变
func TestName(t *testing.T) {
a := "111"
go func() {
time.Sleep(time.Second * 1)
fmt.Println(a)
}()
a = "222"
time.Sleep(time.Second * 2)
}
输出:”222“
如果是同一个自由变量
在并发的情况下,需要特别注意这点。
四、并发与for循环的场景下,闭包作用域
func TestFor(t *testing.T) {
for i := 0; i < 10; i++ {
go func() {
fmt.Println(i)
}()
}
time.Sleep(time.Second * 1)
}
输出:
=== RUN TestFor
10
10
10
10
10
10
10
10
10
4
--- PASS: TestFor (1.00s)
在这里闭包的组成有 自由变量i
以及匿名的函数 func()
以及并发原语go
。
首先我们看一下自由变量i
的作用域范围,为整个for循环的过程。
虽然这里开了10个goroutine并发协程,但他们其实都共用 同一个 自由变量i
。
在打印执行的时候,此时的自由变量i
为此时所在的for循环中的自由变量i
值。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。