golang 中的閉包和defer
golang中的defer和閉包對很多初學者來說,有時候有很多坑,但是很多介紹的文章有寫的亂七八糟.放假了沒事可幹,就稍微總結一下.
閉包
閉包有叫匿名函式,使用閉包可以使我們的程式碼更加優雅簡潔,顧名思義匿名函式就是沒有名字的函式.
func test() int { var i int f:=func() { time.Sleep(1*time.Second) i += 1 //閉包是通過指標傳遞的,所以對i的修改相當於修改指標指向的內容. fmt.Println(" i:", i) } f() f() fmt.Println(i) return i }
注意上面的程式碼,上面的程式碼輸出
i: 1 i: 2 2 2
因為閉包中對a中的變數i是通過指標傳遞的,所以閉包裡面對i的修改會直接修改i的值.
defer
func a() int { var i int defer add(i) //這裡雖然defer是在return之前執行,但是在定義的時候, // 已經將defer要執行的函式壓入棧,所以傳遞給add的是var i int的i值. /*defer func(){ add(i) }() */ i += 100 return i //return0 } func b() int { var i int defer func(){ add(i) }() i += 100 return i //return0 } add(i int){ i += 1 fmt.Println(i) }
分別考慮一下a b 函式的輸出
這裡會產生不同是因為,如果你在定義defer的時候,就要將defer後面的函式引數等入棧,等到ruturn之前的時候出棧執行,a中是將i的拷貝直接入棧,b中通過一個閉包呼叫,實際上將i的指標傳遞給閉包,閉包讀取值拷貝給add.
另外我們經常說defer定義的順序跟執行的順序相反也是因為棧中先入後出的原因.
返回值
上面主要說了defer 和閉包,defer 容易讓人混淆的地方其實是在處理返回值的時候.
func d() (i int) { i = 100 defer func() { i += 1 }() return 5 // 這裡返回之前相當於 i=5 執行defer return i,所以defer中通過閉包給i++ 相當於{i=5 ,i++,return i} } func b() int { var i int defer func() { i += 1 }() i += 100 return i //這裡相當於a=i defer return a 所以defer中閉包i++ 相當於{a=i i++ return a} }
考慮一下上面2個函式的返回值,
b函式中返回retrun i 這個語句相當於如下
- a=i(這裡i值為100)//因為沒有定義返回值名,會定義一個int 的值儲存i返回.
- defer定義的函數出棧,然後執行.在b中通過閉包對i進行了+1
-
return a
所以返回值是100而不是101,因為在第一步a的值是100
d 函式中return 5 這個語句相當於如下 - i=5 //因為d函式中定義了返回值i
- defer定義的函數出棧,然後執行.在b中通過閉包對i進行了+1
-
return i
所以返回值是6而不是5.
總結
其實理解defer 閉包這些只要理解以下幾點就會徹底明白
- 閉包是通過指標操作母函式中的變數
- defer 定義的過程就相當生成一個函式體然後將它壓入棧,等到return的時候出棧執行
- defer 執行是在return 之前執行。這裡要注意的是如果沒有宣告返回變數,需要先宣告一個返回值然後賦值給它返回。