具体定义这里不做累述了,主要看看两则放在一起使用的情况.
闭包里面的参数
先看一段代码:
package main
import (
"fmt"
)
func main() {
fmt.Println(test())
}
func test() int {
var i int
f := func() {
i += 1 //闭包是通过指针传递的,所以对i的修改相当于修改指针指向的内容.
fmt.Println("i:", i)
}
f()
f()
fmt.Println("test:", i)
return i
}
复制代码
打印结果:
D:\workspace\go\src\test>go run main.go
i: 1
i: 2
test: 2
2
复制代码
因为闭包中对变量i是通过指针传递的,所以闭包里面对i的修改会直接修改i的值。
defer和普通函数配合
defer特性:
1. 关键字 defer 用于注册延迟调用。
2. 这些调用直到 return 前才被执。因此,可以用来做资源清理。
3. 多个defer语句,按先进后出的方式执行。
4. defer语句中的变量,在defer声明时就决定了。
复制代码
defer用途:
1. 关闭文件句柄
2. 锁资源释放
3. 数据库连接释放
复制代码
代码1:
package main
func add(x, y int) (z int) {
defer func() {
println(z+9) // 输出: 212
}()
z = x + y
return z + 200 // 执行顺序: (z = z + 200) -> (call defer) -> (return)
}
func main() {
println(add(1, 2)) // 输出: 203
}
复制代码
结果【当函数写了返回值的时候
(z int),return z+20,会先运行z=z+20再运行defer,最后return
】:
D:\workspace\go\src\test>go run main.go
212
203
复制代码
代码2:
package main
import (
"fmt"
)
func main() {
fmt.Println(a())
}
func a() int {
var i int
defer add(i) //延迟调用add(),在程序计数器走到defer时,会把defer右边最外层函数的参数计算完毕,也就是此时的i是多少就是多少
i += 100
return i
}
func add(i int) {
i += 1
fmt.Println(i)
}
复制代码
结果:
D:\workspace\go\src\test>go run main.go
1
100
复制代码
defer和闭包函数配合
代码:
package main
import (
"fmt"
)
func main() {
fmt.Println(b())
}
func b() int {
var i int
defer func() {
add(i) //可以理解为传的是i的指针,{}里面的函数体入栈,i此时不计算,最后是什么就是什么
}()
i += 100
return i //return 之前运行defer,此时的defer里面的i是100,调用add变为101
}
func add(i int) {
i += 1
fmt.Println(i)
}
复制代码
结果:
D:\workspace\go\src\test>go run main.go
101
100
复制代码
这里和普通函数产生不同是因为,如果你在定义defer的时候,就要将defer后面的函数参数等入栈,等到ruturn之前的时候出栈执行,a方法中是将i的拷贝直接入栈,b方法中通过一个闭包调用,实际上将i的指针传递给闭包,闭包读取值拷贝给add。
在程序计数器走到defer时,会把defer右边最外层函数的参数计算完毕,并传递进函数里,但不会执行函数体**(匿名函数的{}里面)**的代码直到包裹defer的函数返回。
另外我们经常说defer定义的顺序跟执行的顺序相反也是因为栈中先入后出的原因.
总结
其实理解defer 闭包这些只要理解以下几点就会彻底明白
-
闭包是通过指针操作母函数中的变量
-
defer 定义的过程就相当生成一个函数体然后将它压入栈,等到return的时候出栈执行
-
defer 执行是在return 之前执行。这里要注意的是如果没有声明返回变量,需要先声明一个返回值然后赋值给它返回。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END