golang中的defer和閉包對很多初學者來說,有時候有很多坑,但是很多介紹的文章有寫的亂七八糟.放假了沒事可干,就稍微總結一下.
閉包
閉包有叫匿名函數(shù),使用閉包可以使我們的代碼更加優(yōu)雅簡潔,顧名思義匿名函數(shù)就是沒有名字的函數(shù).
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之前執(zhí)行,但是在定義的時候,
// 已經將defer要執(zhí)行的函數(shù)壓入棧,所以傳遞給add的是var i int的i值.
/*defer func(){
add(i)
}() */
i += 100
return i //return 0
}
func b() int {
var i int
defer func(){
add(i)
}()
i += 100
return i //return 0
}
add(i int) {
i += 1
fmt.Println(i)
}
分別考慮一下a b 函數(shù)的輸出
1
100
這里會產生不同是因為,如果你在定義defer的時候,就要將defer后面的函數(shù)參數(shù)等入棧,等到ruturn之前的時候出棧執(zhí)行,a中是將i的拷貝直接入棧,b中通過一個閉包調用,實際上將i的指針傳遞給閉包,閉包讀取值拷貝給add.
另外我們經常說defer定義的順序跟執(zhí)行的順序相反也是因為棧中先入后出的原因.
返回值
上面主要說了defer 和閉包,defer 容易讓人混淆的地方其實是在處理返回值的時候.
func d() (i int) {
i = 100
defer func() {
i += 1
}()
return 5 // 這里返回之前相當于 i=5 執(zhí)行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個函數(shù)的返回值,
100
6
b函數(shù)中返回retrun i 這個語句相當于如下
- a=i(這里i值為100) //因為沒有定義返回值名,會定義一個int 的值保存i返回.
- defer定義的函數(shù)出棧,然后執(zhí)行.在b中通過閉包對i進行了+1
- return a
所以返回值是100而不是101,因為在第一步a的值是100
d 函數(shù)中return 5 這個語句相當于如下 - i=5 //因為d函數(shù)中定義了返回值i
- defer定義的函數(shù)出棧,然后執(zhí)行.在b中通過閉包對i進行了+1
- return i
所以返回值是6而不是5.
總結
其實理解defer 閉包這些只要理解以下幾點就會徹底明白
- 閉包是通過指針操作母函數(shù)中的變量
- defer 定義的過程就相當生成一個函數(shù)體然后將它壓入棧,等到return的時候出棧執(zhí)行
- defer 執(zhí)行是在return 之前執(zhí)行。這里要注意的是如果沒有聲明返回變量,需要先聲明一個返回值然后賦值給它返回。