1. 程式人生 > 其它 >Go xmas2020 全英課程 09 學習筆記、Closures

Go xmas2020 全英課程 09 學習筆記、Closures

課程地址 go-class-slides/xmas-2020 at trunk · matt4biz/go-class-slides (github.com)

主講老師 Matt Holiday

09-Closures

變數的生命週期可以超過變數宣告上下文的範圍


左側 f 只是函式指標,右側 f 則是閉包。


Slice 需要一個特定的閉包簽名函式。在閉包的上下文中,我唯一傳遞給我的閉包是 i、j 他們是整數,ss 也是這個函式的一部分雖然沒有被明確傳入。


package main

import "fmt"

func do(d func()) {
	d()
}

func main() {
	for i := 0; i < 4; i++ {
		v := func() {
			fmt.Printf("%d @ %p\n", i, &i)
		}
		do(v)
	}
}
0 @ 0xc000016088
1 @ 0xc000016088
2 @ 0xc000016088
3 @ 0xc000016088

package main

import "fmt"

func main() {
	s := make([]func(), 4)
	for i := 0; i < 4; i++ {
		s[i] = func() {
			fmt.Printf("%d @ %p\n", i, &i)
		}
	}

	for i := 0; i < 4; i++ {
		s[i]()
	}
}
4 @ 0xc000016088
4 @ 0xc000016088
4 @ 0xc000016088
4 @ 0xc000016088

當封閉 i 變數時,每個閉包需要一個引用。四個匿名函式引用的都是同一個 i

,在第一個迴圈退出後,i 值為 4。i 並沒有被垃圾回收,因為它仍被 4 個匿名閉包函式所引用。每次列印都是 4

比如傳入一個閉包函式作為回撥函式的時候,所引用的值在回撥執行前會發生改變,那會出現大問題。


在第一個迴圈內建立一個新變數,每次迴圈宣告初始化一個新變數,每個閉包函式會引用這個新變數,每個 i2 地址不一樣。

	for i := 0; i < 4; i++ {
		i2 := i // closure capture
		s[i] = func() {
			fmt.Printf("%d @ %p\n", i, &i)
		}

閉包是一種函式,呼叫具有來自函式外部的附加資料。

例如資料來自另一個函式的範圍,並且它通過引用封閉(封蓋)。被封閉引數有點像引數,但它並不是,它允許我們函式使用那些不能用引數傳遞的額外資料,例如有些被其他型別固定的資料而無法被傳遞的資料。我們需要注意 gotcha ,因為閉包通過引用封閉,如果閉包是非同步執行的,那麼我封閉(封蓋)的變數可能會發生改變。正如前面的例子,修復方法就是建立一個對應的本地副本,讓閉包函式關閉(封蓋)本地副本,這樣副本的值就固定了。