Golang中defer的三個實戰要點

前言

Golang中的defer是使用頻次比較高的,能創造出延遲生效特效的一種方式。bash

defer也有本身的矯情,須要注意的。 本文將從經過代碼的方式來講明defer的三點矯情。函數

1.defer的生效順序 2.defer與return,函數返回值之間的順序 3.defer定義和執行兩個步驟,作的事情。ui

正文

1.defer的生效順序

先說結論:defer的執行順序是倒序執行(同入棧先進後出)spa

func main() {
	defer func() {
		fmt.Println("我後出來")
	}()
	defer func() {
		fmt.Println("我先出來")
	}()
}
複製代碼

執行後打印出:指針

我先出來
我後出來
複製代碼

2.defer與return,函數返回值之間的順序

先說結論: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值進行了返回。

3.defer定義和執行兩個步驟,作的事情

先說結論:會先將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
}
複製代碼
相關文章
相關標籤/搜索