成人午夜视频全免费观看高清-秋霞福利视频一区二区三区-国产精品久久久久电影小说-亚洲不卡区三一区三区一区

Go閉包的原理和應(yīng)用

這篇文章主要介紹“Go 閉包的原理和應(yīng)用”,在日常操作中,相信很多人在Go 閉包的原理和應(yīng)用問(wèn)題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”Go 閉包的原理和應(yīng)用”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!

資溪網(wǎng)站制作公司哪家好,找成都創(chuàng)新互聯(lián)!從網(wǎng)頁(yè)設(shè)計(jì)、網(wǎng)站建設(shè)、微信開(kāi)發(fā)、APP開(kāi)發(fā)、響應(yīng)式網(wǎng)站建設(shè)等網(wǎng)站項(xiàng)目制作,到程序開(kāi)發(fā),運(yùn)營(yíng)維護(hù)。成都創(chuàng)新互聯(lián)成立于2013年到現(xiàn)在10年的時(shí)間,我們擁有了豐富的建站經(jīng)驗(yàn)和運(yùn)維經(jīng)驗(yàn),來(lái)保證我們的工作的順利進(jìn)行。專(zhuān)注于網(wǎng)站建設(shè)就選成都創(chuàng)新互聯(lián)。

 本文從一道題引出 Go 中的閉包。以下代碼輸出什么?

package main  import "fmt"  func app() func(string) string {  t := "Hi"  c := func(b string) string {   t = t + " " + b   return t  }  return c }  func main() {  a := app()  b := app()  a("go")  fmt.Println(b("All")) }

這道題目答對(duì)的人蠻多的:60%。不管你是答對(duì)還是答錯(cuò),如果最后再加一行代碼:fmt.Println(a("All")),它輸出什么?想看看你是不是蒙對(duì)了。(提示:你可以輸出  t 的地址,看看是什么情況。)

01 什么是閉包

維基百科對(duì)閉包的定義:

在計(jì)算機(jī)科學(xué)中,閉包(英語(yǔ):Closure),又稱(chēng)詞法閉包(Lexical Closure)或函數(shù)閉包(function  closures),是在支持頭等函數(shù)的編程語(yǔ)言中實(shí)現(xiàn)詞法綁定的一種技術(shù)。閉包在實(shí)現(xiàn)上是一個(gè)結(jié)構(gòu)體,它存儲(chǔ)了一個(gè)函數(shù)(通常是其入口地址)和一個(gè)關(guān)聯(lián)的環(huán)境(相當(dāng)于一個(gè)符號(hào)查找表)。環(huán)境里是若干對(duì)符號(hào)和值的對(duì)應(yīng)關(guān)系,它既要包括約束變量(該函數(shù)內(nèi)部綁定的符號(hào)),也要包括自由變量(在函數(shù)外部定義但在函數(shù)內(nèi)被引用),有些函數(shù)也可能沒(méi)有自由變量。閉包跟函數(shù)最大的不同在于,當(dāng)捕捉閉包的時(shí)候,它的自由變量會(huì)在捕捉時(shí)被確定,這樣即便脫離了捕捉時(shí)的上下文,它也能照常運(yùn)行。捕捉時(shí)對(duì)于值的處理可以是值拷貝,也可以是名稱(chēng)引用,這通常由語(yǔ)言設(shè)計(jì)者決定,也可能由用戶自行指定(如  C++)。

關(guān)于(函數(shù))閉包,有幾個(gè)關(guān)鍵點(diǎn):

  • 函數(shù)是一等公民;

  • 閉包所處環(huán)境,可以引用環(huán)境里的值;

問(wèn)到什么是閉包時(shí),網(wǎng)上一般這么回答的:

在支持函數(shù)是一等公民的語(yǔ)言中,一個(gè)函數(shù)的返回值是另一個(gè)函數(shù),被返回的函數(shù)可以訪問(wèn)父函數(shù)內(nèi)的變量,當(dāng)這個(gè)被返回的函數(shù)在外部執(zhí)行時(shí),就產(chǎn)生了閉包。

所以,上面題目中,函數(shù) app 的返回值是另一個(gè)函數(shù),因此產(chǎn)生了閉包。

02 Go 中的閉包

Go 中的函數(shù)是一等公民,之前寫(xiě)過(guò)一篇文章:函數(shù)是一等公民,這到底在說(shuō)什么?

日常開(kāi)發(fā)中,閉包是很常見(jiàn)的。舉幾個(gè)例子。

標(biāo)準(zhǔn)庫(kù)

在 net/http 包中的函數(shù) ProxyURL,實(shí)現(xiàn)如下:

// ProxyURL returns a proxy function (for use in a Transport) // that always returns the same URL. func ProxyURL(fixedURL *url.URL) func(*Request) (*url.URL, error) {  return func(*Request) (*url.URL, error) {   return fixedURL, nil  } }

它的返回值是另一個(gè)函數(shù),簽名是:

func(*Request) (*url.URL, error)

在返回的函數(shù)中,引用了父函數(shù)(ProxyURL)的參數(shù) fixedURL,因此這是閉包。

Web 中間件

在 Web 開(kāi)發(fā)中,中間件一般都會(huì)使用閉包。比如 Echo 框架中的一個(gè)中間件:

// BasicAuthWithConfig returns an BasicAuth middleware with config. // See `BasicAuth()`. func BasicAuthWithConfig(config BasicAuthConfig) echo.MiddlewareFunc {  // Defaults  if config.Validator == nil {   panic("echo: basic-auth middleware requires a validator function")  }   ...  return func(next echo.HandlerFunc) echo.HandlerFunc {   return func(c echo.Context) error {    /// 省略很多代碼       ...   }  } }

首先,echo.MiddlewareFunc 是一個(gè)函數(shù):

type MiddlewareFunc func(HandlerFunc) HandlerFunc

而 echo.HandlerFunc 也是一個(gè)函數(shù):

type HandlerFunc func(Context) error

所以,上面的函數(shù)嵌套了幾層,是典型的閉包。

這是閉包嗎?

在 Go 中不支持函數(shù)嵌套定義,函數(shù)內(nèi)嵌套函數(shù),必須通過(guò)匿名函數(shù)的形式。匿名函數(shù)在 Go 中是很常見(jiàn)的,比如開(kāi)啟一個(gè)  goroutine,通常通過(guò)匿名函數(shù)。

現(xiàn)在有一個(gè)問(wèn)題,以下代碼是閉包嗎?

package main  import (       "fmt" )  func main() {       a := 5     func() {         fmt.Println("a =", a)     }() }

如果按照上面網(wǎng)上一般的回答,這不是閉包,因?yàn)椴](méi)有返回函數(shù)。但按照維基百科的定義,這個(gè)屬于閉包。有沒(méi)有其他證據(jù)呢?

在 Go 語(yǔ)言規(guī)范中,關(guān)于函數(shù)字面值(匿名函數(shù))有這么一句話:

Function literals are closures: they may refer to variables defined in a  surrounding function. Those variables are then shared between the surrounding  function and the function literal, and they survive as long as they are  accessible.

也就是說(shuō),函數(shù)字面值(匿名函數(shù))是閉包,它們可以引用外層函數(shù)定義的變量。

此外,在官方 FAQ 中有這樣的說(shuō)明:

What happens with closures running as goroutines?

例子是:

func main() {     done := make(chan bool)      values := []string{"a", "b", "c"}     for _, v := range values {         go func() {             fmt.Println(v)             done <- true         }()     }      // wait for all goroutines to complete before exiting     for _ = range values {         <-done     } }

這是 Go 中很常見(jiàn)的代碼(很容易寫(xiě)錯(cuò)的),F(xiàn)AQ 稱(chēng)開(kāi)啟 goroutine 的那個(gè)匿名函數(shù)是一個(gè)閉包。

03 匯編看看實(shí)現(xiàn)

回到開(kāi)始的題目,我們通過(guò)匯編看看,Go  閉包的實(shí)現(xiàn),是不是按照維基百科說(shuō)的,「閉包在實(shí)現(xiàn)上是一個(gè)結(jié)構(gòu)體,它存儲(chǔ)了一個(gè)函數(shù)(通常是其入口地址)和一個(gè)關(guān)聯(lián)的環(huán)境(相當(dāng)于一個(gè)符號(hào)查找表)」。

$ go tool compile -S main.go

看關(guān)鍵代碼:

0x0000 00000 (main.go:5) TEXT "".app(SB), ABIInternal, $24-8 0x0000 00000 (main.go:5) MOVQ (TLS), CX 0x0009 00009 (main.go:5) CMPQ SP, 16(CX) 0x000d 00013 (main.go:5) PCDATA $0, $-2 0x000d 00013 (main.go:5) JLS 96 0x000f 00015 (main.go:5) PCDATA $0, $-1 0x000f 00015 (main.go:5) SUBQ $24, SP 0x0013 00019 (main.go:5) MOVQ BP, 16(SP) 0x0018 00024 (main.go:5) LEAQ 16(SP), BP 0x001d 00029 (main.go:5) FUNCDATA $0, gclocals&middot;2a5305abe05176240e61b8620e19a815(SB) 0x001d 00029 (main.go:5) FUNCDATA $1, gclocals&middot;33cdeccccebe80329f1fdbee7f5874cb(SB) 0x001d 00029 (main.go:7) LEAQ type.noalg.struct { F uintptr; "".t string }(SB), AX 0x0024 00036 (main.go:7) MOVQ AX, (SP) 0x0028 00040 (main.go:7) PCDATA $1, $0 0x0028 00040 (main.go:7) CALL runtime.newobject(SB) 0x002d 00045 (main.go:7) MOVQ 8(SP), AX 0x0032 00050 (main.go:7) LEAQ "".app.func1(SB), CX 0x0039 00057 (main.go:7) MOVQ CX, (AX) 0x003c 00060 (main.go:7) MOVQ $2, 16(AX) 0x0044 00068 (main.go:7) LEAQ go.string."Hi"(SB), CX 0x004b 00075 (main.go:7) MOVQ CX, 8(AX) 0x004f 00079 (main.go:10) MOVQ AX, "".~r0+32(SP) 0x0054 00084 (main.go:10) MOVQ 16(SP), BP 0x0059 00089 (main.go:10) ADDQ $24, SP 0x005d 00093 (main.go:10) RET 0x005e 00094 (main.go:10) NOP

其中 LEAQ type.noalg.struct { F uintptr; "".t string }(SB), AX 這行表明 Go  對(duì)閉包的實(shí)現(xiàn)和維基百科說(shuō)的類(lèi)似。

現(xiàn)在看看下面這種是不是這么實(shí)現(xiàn)的:

package main  import (       "fmt" )  func main() {       a := 5     func() {         fmt.Println("a =", a)     }() }

看看匯編

$ go tool compile -S test.go "".main.func1 STEXT size=215 args=0x8 locals=0x50 funcid=0x0   0x0000 00000 (test.go:9) TEXT "".main.func1(SB), ABIInternal, $80-8   0x0000 00000 (test.go:9) MOVQ (TLS), CX   0x0009 00009 (test.go:9) CMPQ SP, 16(CX)   0x000d 00013 (test.go:9) PCDATA $0, $-2   0x000d 00013 (test.go:9) JLS 205   0x0013 00019 (test.go:9) PCDATA $0, $-1   0x0013 00019 (test.go:9) SUBQ $80, SP   0x0017 00023 (test.go:9) MOVQ BP, 72(SP)   0x001c 00028 (test.go:9) LEAQ 72(SP), BP   0x0021 00033 (test.go:9) FUNCDATA $0, gclocals&middot;69c1753bd5f81501d95132d08af04464(SB)   0x0021 00033 (test.go:9) FUNCDATA $1, gclocals&middot;9fb7f0986f647f17cb53dda1484e0f7a(SB)   0x0021 00033 (test.go:10) MOVQ "".a+88(SP), AX   0x0026 00038 (test.go:10) MOVQ AX, (SP)   0x002a 00042 (test.go:10) PCDATA $1, $0   0x002a 00042 (test.go:10) CALL runtime.convT64(SB)   0x002f 00047 (test.go:10) MOVQ 8(SP), AX   0x0034 00052 (test.go:10) MOVQ AX, ""..autotmp_21+64(SP)   0x0039 00057 (test.go:10) LEAQ type.[2]interface {}(SB), CX   0x0040 00064 (test.go:10) MOVQ CX, (SP)   0x0044 00068 (test.go:10) PCDATA $1, $1   0x0044 00068 (test.go:10) CALL runtime.newobject(SB)   0x0049 00073 (test.go:10) MOVQ 8(SP), AX   0x004e 00078 (test.go:10) LEAQ type.string(SB), CX   0x0055 00085 (test.go:10) MOVQ CX, (AX)   0x0058 00088 (test.go:10) LEAQ ""..stmp_1(SB), CX   0x005f 00095 (test.go:10) MOVQ CX, 8(AX)   0x0063 00099 (test.go:10) LEAQ type.int(SB), CX   0x006a 00106 (test.go:10) MOVQ CX, 16(AX)   0x006e 00110 (test.go:10) PCDATA $0, $-2   0x006e 00110 (test.go:10) CMPL runtime.writeBarrier(SB), $0   0x0075 00117 (test.go:10) JNE 189   0x0077 00119 (test.go:10) MOVQ ""..autotmp_21+64(SP), CX   0x007c 00124 (test.go:10) MOVQ CX, 24(AX)   0x0080 00128 (test.go:10) PCDATA $0, $-1   0x0080 00128 (test.go:10) PCDATA $1, $-1

發(fā)現(xiàn)并沒(méi)有這樣的結(jié)構(gòu)體,可見(jiàn) Go 對(duì)這種情況做了特殊處理,因?yàn)樗皇侵貜?fù)使用的匿名函數(shù)。

04 總結(jié)

通過(guò)以上的講解,對(duì)閉包應(yīng)該有了更清晰的認(rèn)識(shí)。如果面試中再被問(wèn)到閉包,你可以這么回答:

對(duì)閉包來(lái)說(shuō),函數(shù)在該語(yǔ)言中得是一等公民。一般來(lái)說(shuō),一個(gè)函數(shù)返回另外一個(gè)函數(shù),這個(gè)被返回的函數(shù)可以引用外層函數(shù)的局部變量,這形成了一個(gè)閉包。通常,閉包通過(guò)一個(gè)結(jié)構(gòu)體來(lái)實(shí)現(xiàn),它存儲(chǔ)一個(gè)函數(shù)和一個(gè)關(guān)聯(lián)的上下文環(huán)境。但  Go 語(yǔ)言中,匿名函數(shù)就是一個(gè)閉包,它可以直接引用外部函數(shù)的局部變量,因?yàn)?Go 規(guī)范和 FAQ 都這么說(shuō)了。

到此,關(guān)于“Go 閉包的原理和應(yīng)用”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!

網(wǎng)頁(yè)標(biāo)題:Go閉包的原理和應(yīng)用
當(dāng)前鏈接:http://jinyejixie.com/article30/ghdcpo.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供外貿(mào)建站、搜索引擎優(yōu)化、品牌網(wǎng)站建設(shè)靜態(tài)網(wǎng)站、服務(wù)器托管微信小程序

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

成都網(wǎng)站建設(shè)公司
建水县| 治县。| 和田市| 阜城县| 永新县| 襄樊市| 文登市| 永新县| 绥化市| 二手房| 丰镇市| 阳原县| 内江市| 来安县| 永宁县| 南岸区| 灵丘县| 永平县| 宜兴市| 许昌市| 仲巴县| 建宁县| 武定县| 亚东县| 朔州市| 滨州市| 苗栗市| 甘南县| 团风县| 分宜县| 张家港市| 兴文县| 行唐县| 广饶县| 阳山县| 江永县| 永康市| 阿克| 定结县| 南部县| 清水河县|