基本概念
defer是Go语言中的一个关键字,作用在函数作用域中,用于在函数结束之前执行特定代码逻辑。
核心特性
1. 多个defer的执行顺序
多个defer之间采用栈结构(FILO):先进后出
func main() {
defer fmt.Println("First")
defer fmt.Println("Second")
defer fmt.Println("Third")
}
// 输出: Third -> Second -> First
2. defer与return的执行顺序
return
是函数的最后一条执行语句(但函数作用域尚未结束)defer
在函数作用域结束时触发执行顺序:先执行
return
→ 再执行defer
func test() int {
defer fmt.Println("defer executed")
return 1 // 先执行return
}
// 函数实际结束前执行defer
3. 函数返回值初始化
命名返回值在函数开始时初始化(零值)
func DeferFunc1(i int) (t int) {
fmt.Println("t = ", t) // 输出: t = 0 (初始值)
return 2
}
// 实际返回值 = 2(覆盖初始值)
4. 命名返回值与defer
defer可以修改命名返回值
func DeferFunc2() (t int) {
defer func() {
t = t * 10 // 修改返回值
}()
return 1 // 相当于: t = 1 → 执行defer → 返回t
}
// 输出: 10
5. defer与panic
规则:
panic后强制执行已注册的defer
panic之后的defer不再执行
即使触发panic,panic之前的defer仍会执行
func main() {
defer fmt.Println("This will print") // 会执行
panic("something wrong")
defer fmt.Println("This won't print") // 不会执行
}
6. defer中嵌套panic
func main() {
defer func() {
if err := recover(); err != nil {
fmt.Println(err) // 捕获最近的panic
} else {
fmt.Println("fatal")
}
}()
defer func() {
panic("defer panic") // 覆盖原始panic
}()
panic("main panic")
}
// 输出: defer panic
执行流程:
panic("main panic")
触发执行已注册的defer(按FILO顺序)
第二个defer执行
panic("defer panic")
覆盖前一个第一个defer中recover捕获到最新panic
7. defer的函数参数包含子函数
参数在defer语句注册时立即求值
func main() {
defer DeferFunc(1, DeferFunc(3, 0)) // 先计算参数
defer DeferFunc(2, DeferFunc(4, 0)) // 先计算参数
}
func DeferFunc(index int, value int) int {
fmt.Print(index)
return index
}
// 输出顺序: 3 → 4 → 2 → 1
原因:
注册第一个defer时先计算参数
DeferFunc(3,0)
→ 打印3
注册第二个defer时先计算参数
DeferFunc(4,0)
→ 打印4
函数结束时执行defer:先进后出 → 先执行第二个defer(打印
2
)→ 再执行第一个defer(打印1
)
8. 经典案例分析
func DeferFunc() (t int) {
defer func(i int) {
fmt.Print(i) // i=0(注册时传参值)
fmt.Print(t) // t=2(函数结束时的值)
}(t) // t=0在此刻传入
t = 1
return 2 // t被赋值为2 → 然后执行defer
}
// 输出: 0 2
关键点:
参数
t
在defer注册时值拷贝(此时t=0)内部函数访问的是当前作用域的t变量(执行时t=2)
defer的执行机制
三个关键时机:
注册时机:函数执行到defer语句时
参数值被确定(值拷贝)
函数被放入调用栈
执行时机:函数return之后,返回之前
可以访问/修改命名返回值
按照后进先出顺序执行
作用域:在定义defer的函数作用域内
能访问外层函数的变量
值可能被后续代码修改
实际应用建议
资源清理:文件关闭、锁释放等
file, _ := os.Open("file.txt") defer file.Close() // 确保文件关闭
避免在循环中使用:
// 错误:所有defer共享相同的变量值 for i := 0; i < 5; i++ { defer fmt.Println(i) // 全部输出4 } // 正确:使用函数参数拷贝值 for i := 0; i < 5; i++ { defer func(i int) { fmt.Println(i) }(i) }
避免长耗时操作:
// 不宜在defer中执行耗时操作 defer func() { time.Sleep(2 * time.Second) // 延迟函数退出 }()