[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
```
- Golang简介
- 开发环境
- Golang安装
- 编辑器及快捷键
- vscode插件
- 第一个程序
- 基础数据类型
- 变量及匿名变量
- 常量与iota
- 整型与浮点型
- 复数与布尔值
- 字符串
- 运算符
- 算术运算符
- 关系运算符
- 逻辑运算符
- 位运算符
- 赋值运算符
- 流程控制语句
- 获取用户输入
- if分支语句
- for循环语句
- switch语句
- break_continue_goto语法
- 高阶数据类型
- pointer指针
- array数组
- slice切片
- slice切片扩展
- map映射
- 函数
- 函数定义和调用
- 函数参数
- 函数返回值
- 作用域
- 函数形参传递
- 匿名函数
- 高阶函数
- 闭包
- defer语句
- 内置函数
- fmt
- strconv
- strings
- time
- os
- io
- 文件操作
- 编码
- 字符与字节
- 字符串
- 读写文件
- 结构体
- 类型别名和自定义类型
- 结构体声明
- 结构体实例化
- 模拟构造函数
- 方法接收器
- 匿名字段
- 嵌套与继承
- 序列化
- 接口
- 接口类型
- 值接收者和指针接收者
- 类型与接口对应关系
- 空接口
- 接口值
- 类型断言
- 并发编程
- 基本概念
- goroutine
- channel
- select
- 并发安全
- 练习题
- 第三方库
- Survey
- cobra