1. 程式人生 > >go語言學習--go中閉包

go語言學習--go中閉包

Go語言支援匿名函式,即函式可以像普通變數一樣被傳遞或使用。

使用方法如下:

package main

import (
    "fmt"
)

func main() {
    var v func(a int) int
    v = func(a int) int {
        return a * a
    }
    fmt.Println(v(6))
//兩種寫法
    v1 := func(i int) int {
        return i * i

    }
    fmt.Println(v1(7))
}

  

GO語言的匿名函式就是閉包,以下是《GO語言程式設計》中對閉包的解釋

 基本概念
閉包是可以包含自由(未繫結到特定物件)變數的程式碼塊,這些變數不在這個程式碼塊內或者
任何全域性上下文中定義,而是在定義程式碼塊的環境中定義。要執行的程式碼塊(由於自由變數包含
在程式碼塊中,所以這些自由變數以及它們引用的物件沒有被釋放)為自由變數提供繫結的計算環
境(作用域)。
 閉包的價值
閉包的價值在於可以作為函式物件或者匿名函式,對於型別系統而言,這意味著不僅要表示
資料還要表示程式碼。支援閉包的多數語言都將函式作為第一級物件,就是說這些函式可以儲存到
變數中作為引數傳遞給其他函式,最重要的是能夠被函式動態建立和返回。

 

一個函式和與其相關的引用環境,組合而成的實體:

package main

import "fmt"

func main() {
    var f = Adder()
    fmt.Println(f(1), "-")
    fmt.Println(f(20), "-")
    fmt.Println(f(300), "-")

}
func Adder() func(int) int {
    var x int
    return func(delta int) int {
        x += delta
        return x
    }
}

  

使用閉包的注意點

(1)由於閉包會使得函式中的變數都被儲存在記憶體中,記憶體消耗很大,所以不能濫用閉包,否則會造成網頁的效能問題,在IE中可能導致記憶體洩露。解決方法是,在退出函式之前,將不使用的區域性變數全部刪除。

(2)閉包會在父函式外部,改變父函式內部變數的值。所以,如果你把父函式當作物件(object)使用,把閉包當作它的公用方法(Public Method),把內部變數當作它的私有屬性(private value),這時一定要小心,不要隨便改變父函式內部變數的值。

 

閉包的注意點

由於閉包使的函式的變數儲存在內中,會出現值被替換的情況

package main

import "fmt"

func main() {
    var flist []func()
    for i := 0; i < 3; i++ {
        flist = append(flist, func() {
            fmt.Println(i)
        })
    }

    for _, f := range flist {
        f()
    }
}

  我們可以看到結果全是3。為什麼呢?我們可以列印下迴圈中i的地址

package main

import "fmt"

func main() {
	var flist []func()
	for i := 0; i < 3; i++ {
		fmt.Println(&i)

		flist = append(flist, func() {
			fmt.Println(i)
		})
	}

	for _, f := range flist {
		f()
	}
}

  發現執行的結果

0xc0420080a8
0xc0420080a8
0xc0420080a8
3
3
3

  i指向的地址是一樣的,那麼就很明瞭了i的變數被儲存了,最後的迴圈把i的值變成了3

如何改進呢,我們只需要每次初始化一個i就可以了

package main

import "fmt"

func main() {
	var flist []func()
	for i := 0; i < 3; i++ {

		i := i //給i變數重新賦值,
		fmt.Println(i)
		flist = append(flist, func() {
			fmt.Println(i)
		})
	}
	for _, f := range flist {
		f()
	}
}