💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
[TOC] defer是Go语言提供的一种用于注册延迟调用的机制:让函数或语句可以在当前函数执行完毕后(包括通过return正常结束或者panic导致的异常结束)执行。 ## defer语句 ```go func foo1() { fmt.Println("start") defer fmt.Println("This is defer") fmt.Println("end") } func foo2() { fmt.Println("start") defer fmt.Println("This is defer1") defer fmt.Println("This is defer2") defer fmt.Println("This is defer3") fmt.Println("end") } func main() { // 案例一 // 延迟处理defer语句。优先执行下面非defer语句, // 遇到return、panic及函数执行完毕,才开始执行defer语句内容。 foo1() // 案例二 // 多个defer语句,先进后出(堆栈)。 fmt.Printf("%v\n", strings.Repeat("-", 50)) foo2() } ``` ## defer执行时间点 补充前面函数没有说的知识点。 `return` 语句在 golang 语言中,是分为两步执行的。 1. 返回值赋值 2. 执行RET指令 >[info] 如果函数的返回值命名的话,第二步执行RET指令是返回 `函数的返回值命名的变量`。请看下图解释 ![](https://img.kancloud.cn/11/7c/117c5713b7ea8534a7090795071d09c4_820x270.png) defer执行时间是在 `return` 的 **第一步后面,第二步前面** 执行的。且 defer 注册要延迟执行是函数的话,函数的 **实参是确定的值** ,与后面修改该值无关。 这个时间点执行是不是有很多变化呢?没错是的,也衍生出不少面试题。请看以下三个示例。讲解请看示例下面的图片。 示例1 ```go func foo() int { x := 0 defer func() { x++ }() return x // ret = x = 0; x = x+1 = 1; retrun ret } func main() { fmt.Printf("foo function return is: %v\n", foo()) } ``` 示例2 ```go func foo() (i int) { x := 0 defer func() { x++ }() return x // i = x = 0; x = x+1 = 1; retrun i } func main() { fmt.Printf("foo function return is: %v\n", foo()) } ``` 示例3 ```go func foo() (x int) { defer func() { x++ }() return x // ret = x = 0; x = x+1 = 1; retrun x } func main() { fmt.Printf("foo function return is: %v\n", foo()) } ``` ![](https://img.kancloud.cn/92/60/92608a0b94854649e90547e73c7aafbc_1565x658.png) ## 常用使用场景 工作中常用的场景如下: - 操作文件,例如打开文件,defer语句实现关闭 - 连接数据库。例如打开连接,defer语句实现关闭连接 - 连接redis。例如打开连接,defer语句实现关闭连接 - 等等... ## 面试题 ```go func foo1(index string, a, b int) int { ret := a + b fmt.Println(index, a, b, ret) return ret } func main() { x := 1 y := 2 defer foo3("AA", x, foo3("A", x, y)) // defer foo3("AA", 1 , 3) x = 10 defer foo3("BB", x, foo3("B", x, y)) // defer foo3("BB", 10 , 12) y = 20 } ``` ```go ```