defer 語句并不會馬上執(zhí)行,而是會進入一個棧,函數(shù) return 前,會按先進后出(FILO)的順序執(zhí)行。也就是說最先被定義的 defer 語句最后執(zhí)行。先進后出的原因是后面定義的函數(shù)可能會依賴前面的資源,自然要先執(zhí)行;否則,如果前面先執(zhí)行,那后面函數(shù)的依賴就沒有了。
使用 defer 最容易采坑的地方是和帶命名返回參數(shù)的函數(shù)一起使用時。
defer 語句定義時,對外部變量的引用是有兩種方式的,分別是作為函數(shù)參數(shù)和作為閉包引用。作為函數(shù)參數(shù),則在 defer 定義時就把值傳遞給 defer,并被緩存起來;作為閉包引用的話,則會在 defer 函數(shù)真正調(diào)用時根據(jù)整個上下文確定當前的值。避免掉坑的關鍵是要理解這條語句:
return xxx
這條語句并不是一個原子指令,經(jīng)過編譯之后,變成了三條指令:
1. 返回值 = xxx
2. 調(diào)用 defer 函數(shù)
3. 空的 return
1,3 步才是 return 語句真正的命令,第 2 步是 defer 定義的語句,這里就有可能會操作返回值。
- 大家可能注意到,函數(shù) increaseA() 是匿名返回值,返回局部變量,同時 defer 函數(shù)也會操作這個局部變量。對于匿名返回值來說,可以假定有一個變量存儲返回值,比如假定返回值變量為 anony,上面的返回語句可以拆分成以下過程:
annoy = i
i++
return
由于 i 是整型,會將值拷貝給 anony,所以 defer 語句中修改 i 值,對函數(shù)返回值不造成影響,所以返回 0 。
func increaseA() int {
var i int
defer func() {
i++
}()
return i
}
重點觀察:
(1). 是作為函數(shù)參數(shù)和還是作為閉包引用
(2). 是匿名返回值還是帶命名返回參數(shù)的函數(shù)