# 1. defer 基础概念 🌟
defer 是 Go 语言提供的一种用于注册延迟调用的机制,它保证了在函数返回之前,延迟调用一定会被执行。这使得 defer 语句非常适合进行资源清理、文件关闭等操作。
# 1.1 基本使用
func main() { | |
defer fmt.Println("defer") // 后执行 | |
fmt.Println("main") // 先执行 | |
} | |
// 输出: | |
// main | |
// defer |
# 1.2 多个 defer 的执行顺序
defer 语句的执行顺序遵循 LIFO(后进先出)原则,类似于栈的操作。
func main() { | |
defer fmt.Println("1") | |
defer fmt.Println("2") | |
defer fmt.Println("3") | |
} | |
// 输出: | |
// 3 | |
// 2 | |
// 1 |
# defer 的执行时机
defer 语句会在函数返回之前执行,因此可以在函数中做一些清理工作,比如关闭文件、释放资源等。
package main | |
import "fmt" | |
func main() { | |
defer fmt.Println("defer") | |
fmt.Println("main") | |
} |
输出结果为:
main
defer
# defer 的参数
defer 语句的参数在定义时就已经确定,而不是在执行时确定。
在声明 defer 时,其参数会被求值,并保存在 defer 栈中,因此即使之后变量发生变化,defer 调用的参数值不会改变
package main | |
import "fmt" | |
func main() { | |
i := 0 | |
defer fmt.Println(i) | |
i++ | |
} |
输出结果为:
0
# defer 修改返回值的条件
- 当函数有命名返回值时,defer 语句可以修改返回值。
- 当函数没有命名返回值时,defer 语句不能修改返回值。
func main() { | |
fmt.Println("命名返回值:", DeferTest()) | |
fmt.Println("无命名返回值:", DeferTest2()) | |
} | |
// 如果使用命名返回值,defer 可以修改它,从而影响最终返回值 | |
func DeferTest() (result int) { | |
defer func() { | |
result += 1 | |
}() | |
result = 5 | |
return // 等价于 return result | |
} | |
//defer 可以修改局部变量,但不会影响返回值(如果不是命名返回值) | |
func DeferTest2() int { | |
result := 0 | |
defer func() { | |
result += 1 | |
}() | |
result = 5 | |
//return result 会导致 result 被拷贝一份,defer 修改的是拷贝的值,而不是原值 | |
return result | |
} |
输出结果为:
命名返回值: 6
无命名返回值: 5
关键在于:
- 命名返回值:defer 可以修改最终的返回值,因此它直接访问的就是返回值变量
- 无命名返回值:defer 修改的是返回值变量的副本,因此它修改的并不是最终的返回值
# defer 与 return 的执行顺序
func f() int { | |
defer fmt.Println("defer") | |
fmt.Println("return") | |
return 1 | |
} |
输出:
return
defer
总结 :先算 return, 后跑 defer, 再真返回。