最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Go中的閉包函數(shù)Closure示例詳解

 更新時(shí)間:2025年12月22日 10:03:01   作者:bybitq  
閉包是Go語言中非常重要的一個(gè)概念,它允許函數(shù)訪問和操作函數(shù)外部的詞法作用域中的變量,下面這篇文章主要介紹了Go中閉包函數(shù)Closure的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下

前言

“閉包”(Closure)是編程中一個(gè)非常重要、但初學(xué)者容易暈的概念。它在函數(shù)式編程(Functional Programming)中無處不在,Go 語言對它的支持非常強(qiáng)大。

簡單來說,閉包就是:一個(gè)函數(shù)和它周圍環(huán)境的綁定。

為了讓你徹底理解,我們從三個(gè)層面來拆解:通俗定義、代碼演示、以及底層原理。

1. 通俗定義:自帶“背包”的函數(shù)

通常情況下,一個(gè)函數(shù)執(zhí)行完,它內(nèi)部定義的變量就會被銷毀(從內(nèi)存棧中彈出)。

但是閉包不一樣。閉包是一個(gè)特殊的函數(shù),它被創(chuàng)建的時(shí)候,會偷偷把當(dāng)時(shí)它能看到的變量抓取過來,裝進(jìn)自己的“背包”里帶走。

公式:

閉包 = 函數(shù)代碼 + 捕獲的外部變量

哪怕創(chuàng)造它的那個(gè)外部函數(shù)已經(jīng)執(zhí)行結(jié)束了,閉包依然能通過“背包”訪問和修改那些變量。

2. 代碼演示:最經(jīng)典的計(jì)數(shù)器

這是理解閉包的“Hello World”例子。

Go

package main

import "fmt"

// seq 此時(shí)是一個(gè)“工廠”,它返回一個(gè)函數(shù)
// 這個(gè)返回的函數(shù)返回 int
func seq() func() int {
    i := 0 // 這是一個(gè)局部變量,按理說 seq 執(zhí)行完它就該銷毀了
    
    // 返回一個(gè)匿名函數(shù)
    return func() int {
        i++ // 這個(gè)匿名函數(shù)引用了外部的 i
        return i
    }
}

func main() {
    // 1. 創(chuàng)建第一個(gè)閉包實(shí)例 nextNum
    // 此時(shí) seq() 執(zhí)行結(jié)束,但變量 i 被 nextNum 的背包抓走了
    nextNum := seq()

    fmt.Println(nextNum()) // 輸出 1
    fmt.Println(nextNum()) // 輸出 2
    fmt.Println(nextNum()) // 輸出 3 (i 的狀態(tài)被保留了!)

    // 2. 創(chuàng)建第二個(gè)閉包實(shí)例 nextNum2
    // 它會有自己全新的 i,和上面那個(gè)互不干擾
    nextNum2 := seq()
    fmt.Println(nextNum2()) // 輸出 1
}

為什么叫閉包? 注意看 i。它定義在 seq 里,但在 main 里通過 nextNum() 依然能一直修改它。nextNum 這個(gè)函數(shù)把 i “封閉”在自己的作用域里了,這就叫閉包。

3. 底層原理:變量去哪了?(CS 專業(yè)向)

作為一個(gè)計(jì)算機(jī)專業(yè)的學(xué)生,你可能會問:“棧上的局部變量不是函數(shù)返回就銷毀了嗎?為什么 i 還能活下來?”

這里涉及到了 Go 編譯器的逃逸分析 (Escape Analysis)。

  • 正常情況:函數(shù)內(nèi)部的變量分配在棧 (Stack) 上,函數(shù)結(jié)束,棧幀彈出,數(shù)據(jù)銷毀。

  • 閉包情況:編譯器掃描代碼,發(fā)現(xiàn)變量 i 被內(nèi)部的匿名函數(shù)引用了,而且這個(gè)匿名函數(shù)被返回到了外部。

  • 逃逸:編譯器判定 i 不能死在棧上,于是把它分配到了堆 (Heap) 上。

  • 引用:返回的閉包函數(shù)(nextNum)內(nèi)部持有一個(gè)指針,指向堆上的這個(gè) i。

所以,當(dāng)你調(diào)用 makeHandler(viewHandler) 時(shí),那層“中間件”之所以能記住 viewHandler 是誰,就是因?yàn)?nbsp;fn 這個(gè)變量逃逸到了堆上,被返回的那個(gè)匿名函數(shù)死死抓在手里。

4. 閉包在實(shí)戰(zhàn)中的三大用途

回到你的 HTTP 題目,以及以后的開發(fā)中,閉包主要有這就三種用法:

A. 封裝狀態(tài)(像簡化的類)

不需要專門寫一個(gè) struct 或者 class 來存狀態(tài),直接用閉包。上面的計(jì)數(shù)器就是例子。

B. 延遲執(zhí)行 / 回調(diào) (Callback)

當(dāng)你把一個(gè)函數(shù) A 傳給另一個(gè)函數(shù) B,B 在未來某個(gè)時(shí)間點(diǎn)調(diào)用 A,A 還能記得當(dāng)初傳給它的數(shù)據(jù)。

C. 中間件 / 裝飾器 (Middleware / Decorator)

這就是你那段代碼的本質(zhì)。 你想給原本的函數(shù) viewHandler 加一層“安檢”。

  • 輸入:一個(gè)舊函數(shù)。

  • 輸出:一個(gè)新函數(shù)(新函數(shù) = 安檢邏輯 + 舊函數(shù))。

Go

// 這就是一個(gè)典型的裝飾器模式(Decorator Pattern)
func makeHandler(fn ...) http.HandlerFunc {
    return func(...) {
        // 1. 前置邏輯 (Pre-processing)
        // ... 驗(yàn)證 title ...
        
        // 2. 調(diào)用原函數(shù) (Original logic)
        fn(...) 
        
        // 3. 后置邏輯 (Post-processing) - 可選
    }
}

總結(jié)

  • 閉包就是能記住它出生環(huán)境的函數(shù)。

  • 在 Go 里,閉包讓局部變量逃逸到堆上,從而延長了變量的生命周期。

  • 在很多場景下(比如你的 Web 路由處理),用閉包比定義一個(gè) Interface 或者 Struct 更輕量、更優(yōu)雅。

看一段代碼:

package main

import "fmt"

func adder() func(int) int {
	sum := 0
	return func(x int) int {
		sum += x
		return sum
	}
}

func main() {
	pos, neg := adder(), adder()
	for i := 0; i < 10; i++ {
		fmt.Println(
			pos(i),
			neg(-2*i),
		)
	}
}

輸出結(jié)果:

0 0
1 -2
3 -6
6 -12
10 -20
15 -30

每次調(diào)用 makeHandler(工廠),都會產(chǎn)生一個(gè)新的、獨(dú)立的閉包環(huán)境。它們互不干擾,不引用同一個(gè)變量。

為了讓你徹底明白,我們要分兩種情況來看:

情況一:調(diào)用兩次“工廠函數(shù)” (你的 main 函數(shù)里的情況)

這是你在 main 函數(shù)里寫的代碼邏輯:

Go

// 第 1 次調(diào)用 makeHandler
// 產(chǎn)生了一個(gè)閉包 A,它的背包里裝的是 viewHandler
h1 := makeHandler(viewHandler) 

// 第 2 次調(diào)用 makeHandler
// 產(chǎn)生了一個(gè)閉包 B,它的背包里裝的是 editHandler
h2 := makeHandler(editHandler) 

結(jié)論: 它們不引用同一個(gè)變量。

原理: 每次你調(diào)用 makeHandler 時(shí),Go 都會在內(nèi)存中開辟一個(gè)新的棧幀 (Stack Frame)。

  • 第一次調(diào)用時(shí),參數(shù) fn 是 viewHandler。這個(gè) fn 也就是閉包 A “背包”里的東西。

  • 第二次調(diào)用時(shí),參數(shù) fn 是 editHandler。這個(gè) fn 是閉包 B “背包”里的東西。

雖然變量名都叫 fn,但它們在內(nèi)存里是兩塊完全不同的地址。就好比你去了兩次肯德基,第一次買了漢堡,第二次買了薯?xiàng)l。雖然都裝在“打包袋”里,但這是兩個(gè)不同的袋子,裝的東西也不一樣。

情況二:調(diào)用同一個(gè)“閉包實(shí)例”多次

這是當(dāng)服務(wù)器運(yùn)行起來,多個(gè)用戶訪問同一個(gè) URL 時(shí)的情況:

Go

// 假設(shè)這是 main 函數(shù)里注冊好的那個(gè) handler
viewHandlerWrapper := makeHandler(viewHandler)

// 用戶甲訪問 /view/1
viewHandlerWrapper(w1, r1) 

// 用戶乙訪問 /view/2
viewHandlerWrapper(w2, r2)

結(jié)論: 它們引用的是同一個(gè)外部變量。

原理: 因?yàn)?nbsp;viewHandlerWrapper 是同一個(gè)閉包實(shí)例。 在這個(gè)例子里,它們引用的那個(gè)“外部變量”就是被捕獲的 fn (也就是 viewHandler)。 無論用戶調(diào)用多少次 viewHandlerWrapper,它都會去自己的背包里找那個(gè) fn。這也是正確的,因?yàn)槲覀兿M蠹矣玫奶幚磉壿嬍且粯拥摹?/p>

用那個(gè)計(jì)數(shù)器的例子看最清楚

回到剛才的計(jì)數(shù)器 seq,這能最直觀地展示“變量隔離”:

Go

func seq() func() int {
    i := 0 // 這就是那個(gè)“外部變量”
    return func() int {
        i++
        return i
    }
}

func main() {
    // 【工廠調(diào)用兩次】:創(chuàng)造了兩個(gè)獨(dú)立的世界
    counterA := seq() 
    counterB := seq()

    // counterA 的背包里有一個(gè) i (地址 0x1111)
    // counterB 的背包里有一個(gè) i (地址 0x2222)

    fmt.Println(counterA()) // 輸出 1 (修改的是 0x1111)
    fmt.Println(counterA()) // 輸出 2 (修改的是 0x1111)

    // 關(guān)鍵點(diǎn)來了:
    fmt.Println(counterB()) // 輸出 1 (修改的是 0x2222)
    // 發(fā)現(xiàn)了嗎?counterB 從頭開始計(jì)數(shù),完全沒受 counterA 影響!
}

總結(jié)你的 makeHandler 代碼

在你的代碼中:

Go

func makeHandler(fn func(...)) http.HandlerFunc {
    return func(w, r) {
        // ...
        fn(w, r, title) // 這里的 fn 是被捕獲的
    }
}
  • makeHandler(viewHandler) 創(chuàng)建了一個(gè)閉包,它獨(dú)占 viewHandler。

  • makeHandler(editHandler) 創(chuàng)建了另一個(gè)閉包,它獨(dú)占 editHandler。

它們互不干擾,絕對安全。 這也是為什么閉包在 Go 的并發(fā)編程和中間件設(shè)計(jì)中如此好用的原因——它天然地實(shí)現(xiàn)了狀態(tài)的隔離。

到此這篇關(guān)于Go中閉包函數(shù)Closure的文章就介紹到這了,更多相關(guān)Go閉包函數(shù)Closure內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • GoLang sync.Pool簡介與用法

    GoLang sync.Pool簡介與用法

    這篇文章主要介紹了GoLang sync.Pool簡介與用法,Pool是可伸縮、并發(fā)安全的臨時(shí)對象池,用來存放已經(jīng)分配但暫時(shí)不用的臨時(shí)對象,通過對象重用機(jī)制,緩解GC壓力,提高程序性能
    2023-01-01
  • ubuntu安裝golang并設(shè)置goproxy的方法步驟

    ubuntu安裝golang并設(shè)置goproxy的方法步驟

    在Ubuntu系統(tǒng)上安裝Go語言(Golang)有多種方法,包括使用包管理器、從源代碼編譯安裝以及使用版本管理工具如gvm,安裝完成后,為了方便管理Go語言項(xiàng)目依賴,需要設(shè)置GOPATH環(huán)境變量并配置Go代理,本文介紹ubuntu安裝golang并設(shè)置goproxy的方法,感興趣的朋友一起看看吧
    2024-10-10
  • 適合PHP同學(xué)的GoFrame框架使用體驗(yàn)及學(xué)習(xí)建議

    適合PHP同學(xué)的GoFrame框架使用體驗(yàn)及學(xué)習(xí)建議

    這篇文章主要為大家介紹了非常適合PHP同學(xué)使用的GoFrame框架設(shè)計(jì)思想使用體驗(yàn)及學(xué)習(xí)建議介紹,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • Go高級特性探究之協(xié)程池詳解

    Go高級特性探究之協(xié)程池詳解

    在并發(fā)編程中,協(xié)程是?Go?語言的核心特性之一,本文將介紹如何使用?Go?協(xié)程池構(gòu)造一個(gè)協(xié)程池,并解決函數(shù)傳參問題、優(yōu)雅關(guān)閉協(xié)程池和保證協(xié)程安全的問題,感興趣的可以了解一下
    2023-06-06
  • Go語言基礎(chǔ)語法之結(jié)構(gòu)體及方法詳解

    Go語言基礎(chǔ)語法之結(jié)構(gòu)體及方法詳解

    結(jié)構(gòu)體類型可以用來保存不同類型的數(shù)據(jù),也可以通過方法的形式來聲明它的行為。本文將介紹go語言中的結(jié)構(gòu)體和方法,以及“繼承”的實(shí)現(xiàn)方法
    2021-09-09
  • golang中隨機(jī)數(shù)rand的使用

    golang中隨機(jī)數(shù)rand的使用

    本文主要介紹了golang中隨機(jī)數(shù)rand的使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-08-08
  • go語言中值類型和指針類型的深入理解

    go語言中值類型和指針類型的深入理解

    這篇文章主要給大家介紹了關(guān)于go語言中值類型和指針類型的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2022-03-03
  • go語言實(shí)現(xiàn)簡易比特幣系統(tǒng)之交易簽名及校驗(yàn)功能

    go語言實(shí)現(xiàn)簡易比特幣系統(tǒng)之交易簽名及校驗(yàn)功能

    這篇文章主要介紹了go語言實(shí)現(xiàn)簡易比特幣系統(tǒng)之交易簽名及校驗(yàn)功能,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-04-04
  • go語言的sql包原理與用法分析

    go語言的sql包原理與用法分析

    這篇文章主要介紹了go語言的sql包原理與用法,較為詳細(xì)的分析了Go語言里sql包的結(jié)構(gòu)、相關(guān)函數(shù)與使用方法,需要的朋友可以參考下
    2016-07-07
  • Go并發(fā)控制WaitGroup的使用場景分析

    Go并發(fā)控制WaitGroup的使用場景分析

    WaitGroup,可理解為Wait-Goroutine-Group,即等待一組goroutine結(jié)束,本文通過具體場景結(jié)合實(shí)際例子給大家介紹使用WaitGroup控制的實(shí)現(xiàn)方法,感興趣的朋友跟隨小編一起看看吧
    2021-07-07

最新評論

婺源县| 宁陕县| 黄冈市| 南京市| 萨嘎县| 邵武市| 桐庐县| 三亚市| 普宁市| 吉木乃县| 凤冈县| 康平县| 怀宁县| 和静县| 云龙县| 玛多县| 绵竹市| 衡阳市| 大田县| 诸暨市| 巴林右旗| 绥滨县| 南和县| 大邑县| 洮南市| 紫阳县| 竹山县| 平泉县| 仁化县| 乌审旗| 罗定市| 阿拉善盟| 巨鹿县| 东阿县| 礼泉县| 深圳市| 当雄县| 武强县| 常宁市| 小金县| 循化|