这是我参与「第三届青训营 -后端场」笔记创作活动的的第1篇笔记。
Go语言中的defer语句十分常用,其的作用是延迟函数或方法调用语句的执行至函数结束时,我们可以利用defer语句作一些资源的收尾工作,比如:
func A() {
...
f, err := os.Open(filename)
if err != nil { ... }
defer f.close()
...
}
文件资源便会在函数A()执行完后得到释放,通过defer语句我们的编程更加方便,不用刻意记住资源的释放,同时也使代码的可读性上升了。
执行顺序
defer语句的执行顺序不同于普通的语句,它们会被压入栈中延迟执行,因此执行顺序符合后进先出的规律。
在青训营入营笔试中就有一道选择题考到这个点,题目如下:
func main(){
if true {
defer fmt.Printf("1")
} else {
defer fmt.Printf("2")
}
defer fmt.Printf("3")
}
结果为31,因为打印3的语句在后,因此执行时顺序在前,而打印2的语句所在if分支未被执行,因此在函数收尾时其中的defer语句也不会被执行。
与return的关系
我们使用golang编程时经常会遇到在有返回值的函数中用到defer语句,有时候defer语句跟返回值相关,那么什么情况下defer语句会影响到返回值呢?
以下有两个例子:
1.
func A() int {
i := 0
defer func(){ i++ }()
return i
}
2.
func B() (i int) {
defer func(){ i++ }()
return i
}
使用如下main函数:
func main(){
fmt.Println("A(): ", A())
fmt.Println("B(): ", B())
}
运行结果如图:
原理在于:函数结束时,return语句首先对返回值进行赋值,然后执行被defered的语句,最后将返回值带出函数体。
在A()函数中,return会创建一个int类型的匿名变量,将i的值赋给它,那么defer的i++只能影响i而不能影响将要返回的匿名变量,故结果仍为0。
在B()函数中,return的变量在原本已声明为i,那么defer语句的i++则会影响到将要被返回的变量i,因此结果是1。