Defer 的變量快照什么情況會失效?
本篇問題:Go 中閉包的底層原理?
關于 defer 的基本知識點,我在以前的教程中有寫過:14. Go語言流程控制:defer 延遲調用
其中有一個知識是 defer 的變量快照,舉個簡單的例子來說
在下面這段代碼中,會先打印出來 18,即使后面 age 已經被改變了,可 defer 中的 age還是 修改之前的 0,這種現象稱之為變量快照。
- func func1() {
- age := 0
- defer fmt.Println(age) // output: 0
- age = 18
- fmt.Println(age) // output: 18
- }
- func main() {
- func1()
- }
對于這個輸出結果,相信還是挺容易理解的。
接下來,我請大家再看下面這個例子,可以猜猜看會輸出什么?
- func func1() {
- age := 0
- defer func() {
- fmt.Println(age)
- }()
- age = 18
- return
- }
- func main() {
- func1()
- }
正確的答案是:18, 而不是 0
你肯定會納悶:不對啊,defer 不是會對變量的值做一個快照嗎?答案應該是 0 啊,為什么會是 18?
實際上,仔細觀察,可以發現上面的兩個例子的區別就在于,一個 defer 后接的是單個表達式,另一個 defer 后接的是一個函數,并且不是普通函數,而是一個匿名的閉包函數。
根據閉包的特性,實際上在閉包函數存的是 age 這個變量的指針(原因可以查看上一篇文章:Go 面試題 013:Go 中閉包的底層原理是?),因而,在 defer 后所修改的值會直接影響到 defer 中的 age 的值。
總結一下:
- 若 defer 后接的是單行表達式,那defer 中的 age 只是拷貝了 func1 函數棧中 defer 之前的 age 的值;
- 若 defer 后接的是閉包函數,那defer 中的 age 只是存儲的是 func1 函數棧中 age 的指針。
本文轉載自微信公眾號「Go編程時光」,可以通過以下二維碼關注。轉載本文請聯系Go編程時光公眾號。