Golang中的defer是使用頻次比較高的,能創造出延遲生效特效的一種方式。bash
defer也有本身的矯情,須要注意的。 本文將從經過代碼的方式來講明defer的三點矯情。函數
1.defer的生效順序 2.defer與return,函數返回值之間的順序 3.defer定義和執行兩個步驟,作的事情。ui
先說結論:defer的執行順序是倒序執行(同入棧先進後出)spa
func main() {
defer func() {
fmt.Println("我後出來")
}()
defer func() {
fmt.Println("我先出來")
}()
}
複製代碼
執行後打印出:指針
我先出來
我後出來
複製代碼
先說結論:return最早執行->return負責將結果寫入返回值中->接着defer開始執行一些收尾工做->最後函數攜帶當前返回值退出code
返回值的表達方式,咱們知道根據是否提早聲明有兩種方式:一種是func test() int 另外一種是 func test() (i int),因此兩種狀況都來講說string
func test() intit
func main() {
fmt.Println("main:", test())
}
func test() int {
var i int
defer func() {
i++
fmt.Println("defer2的值:", i)
}()
defer func() {
i++
fmt.Println("defer1的值:", i)
}()
return i
}
複製代碼
輸出:io
defer1的值: 1
defer2的值: 2
main: 0
複製代碼
詳解:return的時候已經先將返回值給定義下來了,就是0,因爲i是在函數內部聲明因此即便在defer中進行了++操做,也不會影響return的時候作的決定。function
func test() (i int)
func main() {
fmt.Println("main:", test())
}
func test() (i int) {
defer func() {
i++
fmt.Println("defer2的值:", i)
}()
defer func() {
i++
fmt.Println("defer1的值:", i)
}()
return i
}
複製代碼
輸出:
defer1的值: 1
defer2的值: 2
main: 2
複製代碼
詳解:因爲返回值提早聲明瞭,因此在return的時候決定的返回值仍是0,可是後面兩個defer執行後進行了兩次++,將i的值變爲2,待defer執行完後,函數將i值進行了返回。
先說結論:會先將defer後函數的參數部分的值(或者地址)給先下來【你能夠理解爲()裏頭的會先肯定】,後面函數執行完,纔會執行defer後函數的{}中的邏輯
func test(i *int) int {
return *i
}
func main(){
var i = 1
// defer定義的時候test(&i)的值就已經定了,是1,後面就不會變了
defer fmt.Println("i1 =" , test(&i))
i++
// defer定義的時候test(&i)的值就已經定了,是2,後面就不會變了
defer fmt.Println("i2 =" , test(&i))
// defer定義的時候,i就已經肯定了是一個指針類型,地址上的值變了,這裏跟着變
defer func(i *int) {
fmt.Println("i3 =" , *i)
}(&i)
// defer定義的時候i的值就已經定了,是2,後面就不會變了
defer func(i int) {
//defer 在定義的時候就定了
fmt.Println("i4 =" , i)
}(i)
defer func() {
// 地址,因此後續跟着變
var c = &i
fmt.Println("i5 =" , *c)
}()
// 執行了 i=11 後才調用,此時i值已經是11
defer func() {
fmt.Println("i6 =" , i)
}()
i = 11
}
複製代碼