[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指令是返回 `函數的返回值命名的變量`。請看下圖解釋

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())
}
```

## 常用使用場景
工作中常用的場景如下:
- 操作文件,例如打開文件,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