动态

详情 返回 返回

go的defer和閉包(例子説明,非內部實現) - 动态 详情

用幾個例子説明golang的閉包函數,結合defer使用,配合對應代碼及文末總結。

函數 説明 輸出
e1 defer調用,相當於是拿到了當前err變量的快照,即註冊defer函數的時候,將當下err的值塞入到defer中 start err1
e2 defer 調用,但是一個閉包函數,且閉包函數有傳參,閉包捕獲當前err的值仍然是 start err2(閉包捕獲的是變量值的拷貝),且閉包內的值變量改變不會影響外部err的值(詳見見e5) start err2
e3 defer 調用,閉包內的變量和匿名函數外的變量是公用的,沒有傳遞形參,沒有傳遞形參,與上下文共享 defer3 error
e4 defer 調用,在函數 e4 中,當你將 err 作為參數傳遞給閉包函數時,實際上是創建了一個閉包函數的副本,這個副本在閉包內部獨立於外部作用域。這種行為是因為閉包在捕獲外部變量時,會將外部變量的當前值複製到閉包內部,形成一個閉包環境,現在理解了閉包的概念了吧。具體來説,在 defer 語句執行的時候,閉包函數會將 err 的當前值(即 "start err4")複製到閉包內部的參數中。之後,無論外部作用域的 err 是否發生改變,閉包內部的參數值都會保持不變,因為閉包已經捕獲了一個快照 start err4
e5 傳值的情況下,閉包內的值變量改變不會影響外部err的值,(互相獨立) now err is start err5 start err5CHANGE ME
e6 閉包沒有傳值,拿到的err是最後賦值的, now err is start err6 defer6 error CHANGE ME
package main

import (
    "errors"
    "fmt"
)

func e1(){
    err := errors.New("start err1")
    defer fmt.Println(err)
    err = errors.New("defer1 error")
    return
}

func e2(){
    err := errors.New("start err2")
    defer func(e error) {
        fmt.Println(e)
    }(err)
    err = errors.New("defer2 error")
    return
}

func e3(){
    err := errors.New("start err3")
    //閉包內的變量和匿名函數外的變量是公用的,沒有傳遞形參,沒有傳遞形參,與上下文共享
    defer func() {
        fmt.Println(err)
    }()
    err = errors.New("defer3 error")
    return
}

func e4(){
    var err error
    err = errors.New("start err4")
    //閉包內的變量和匿名函數外的變量是公用的,但是如果傳了形參,那就和上文的共用了
    //在函數 e4 中,當你將 err 作為參數傳遞給閉包函數時,實際上是創建了一個閉包函數的副本,這個副本在閉包內部獨立於外部作用域。這種行為是因為閉包在捕獲外部變量時,會將外部變量的當前值複製到閉包內部,形成一個閉包環境
    //具體來説,在 defer 語句執行的時候,閉包函數會將 err 的當前值(即 "start err4")複製到閉包內部的參數中。之後,無論外部作用域的 err 是否發生改變,閉包內部的參數值都會保持不變,因為閉包已經捕獲了一個快照。
    defer func(err error) {
        fmt.Println(err)
    }(err)
    err = errors.New("defer4 error")
    return
}

func e5(){
    err := errors.New("start err4")

    defer func(err error ) {
        err=errors.New(err.Error()+"CHANGE ME")
        fmt.Println(err)
    }(err)
    fmt.Println("now err is ",err)
    err = errors.New("defer5 error")
    return
}
func e6() {
    err := errors.New("start err6")

    defer func() {
        err = errors.New(err.Error() + " CHANGE ME")
        fmt.Println(err)
    }()

    fmt.Println("now err is ", err)
    err = errors.New("defer6 error")
    return
}

func main(){
    e1()
    e2()
    e3()
    e4()
    e5()
    e6()
}

變量作用域和閉包:

Go 語言中的變量作用域由代碼塊決定。變量在其定義的代碼塊內可見。
閉包是一個函數值,它可以捕獲其定義時周圍的作用域內的變量。
閉包可以在定義之外被調用,仍然訪問並修改捕獲的變量。

閉包和變量捕獲:

閉包函數可以捕獲外部作用域的變量。在閉包內部,它們可以訪問外部變量的值。
閉包捕獲的變量是其副本,即閉包內部使用的是變量值的拷貝。
修改閉包內部捕獲的變量不會影響外部作用域中的變量,除非你在閉包內直接修改外部作用域的變量。

閉包參數傳遞:

在閉包內部接收外部作用域的變量作為參數,可以使閉包操作外部作用域的變量。
使用閉包參數傳遞可以有效隔離閉包內外的變量,從而保持可預測性。

在 defer 中的閉包:

當在 defer 語句中使用閉包時,閉包內部的變量會被“捕獲”並在 defer 執行時使用。
在閉包內部修改閉包捕獲的變量不會影響外部作用域中的變量,除非你直接修改外部作用域的變量。

總結:

閉包是一種強大的概念,可以使函數擁有狀態並延遲執行。
瞭解閉包如何操作變量作用域,以及它們如何捕獲和修改變量,是編寫高效、清晰的 Go 代碼的關鍵。
當在閉包中操作變量時,要注意變量作用域、捕獲的變量副本和對外部作用域的影響。

user avatar melodyne 头像 wujingquan 头像 phpercode 头像 guangmingleiluodebaomihua 头像 huifeideniao 头像 wangjingyu_5f58472234cff 头像 jkdataapi 头像 saxiaoyige 头像 hex 头像
点赞 9 用户, 点赞了这篇动态!
点赞

Add a new 评论

Some HTML is okay.