要弄明白defer首先得搞清楚闭包和局部作用域。
闭包
闭包是匿名函数与匿名函数所引用环境的组合。
看一个例子:
func main() {
n := 0
f := func() {
n += 1
}
f()
fmt.Println("n = ", n)
}
// output: n = 1
func main() {
n := 0
f := func(n int) {
n += 1
}
f(n)
fmt.Println("n = ", n)
}
// output n = 0
看了以上两个例子,大概可以对闭包有个简单的认识,匿名函数可以直接引用上一级函数内的变量, 所以可以看到第一个例子进行 n+1 操作修改了main函数中的n的值。
而第二个函数中的匿名函数需要传参n, 因为go中的传参都是值类型的, 所以不会修改外部变量n 输出扔为0。
这篇文章的重点不是为了介绍闭包,所以这里只介绍这么多,有兴趣可以去网上找相关文章查阅。
继续说defer
- defer调用过程:
- 返回值为 xxx
- 调用defer函数
- return
其实defer的调用相当于一个内部函数的调用,可以看以下例子:
func f() (r int) {
// go函数编译的时候已经将带名字的返回值变量给初始化
// 所以在返回值的时候调用defer函数会改变其值
r = 5
defer func() {
r = r + 5
}()
return
}
// output 10
// 与闭包中的第一个例子类似,defer 函数修改了r的值
// 返回值r = 5
// 调用defer函数 r += 5 => r = 10
// return r => 10
例2
func f() (r int) {
t := 5
defer func() {
t = t + 5
}()
return t
}
// 结合上边说的defer调用过程:
// 返回值 r = t 对r进行赋值操作
// defer 修改 t 的值 t+= 5 => t =10
// return r => r = 5
例3
func f() int {
t := 5
defer func() {
t = t + 5
}()
return t
}
// 同样结合调用过程:
// 返回值赋值 这里的返回值是个匿名变量, 假设这个匿名变量为NoName = t = 5
// defer 调用 t+= 5 => t = 10
// 返回 NoName = 5
例4
func f3() (r int) {
// defer的时候 r为0
defer func(r int) {
r = r + 5
}(r)
return 1
}
// 结合defer调用过程
// 返回值 r 赋值 r = 1
// 带参数r的匿名函数 进行defer调用, 参考闭包中的例子2 知道 r的传入不会影响外部r的值, 所以匿名函数外部的 r扔为1
// 返回 r的值 1
以上, 即为defer的几种调用。
文章内容有所参考, 如下:
Golang之轻松化解defer的温柔陷阱