天天看點

Go 語言 defer(延遲) 關鍵字基本用法詳解

作者:迹憶客
Defer 語句用于在使用defer關鍵字的函數傳回之前執行函數調用。

上面這個說法可能看起來比較複雜,但通過一個例子來了解就會感覺很簡單。

package main

import (  
    "fmt"
)

func finished() {  
    fmt.Println("Finished finding largest")
}

func largest(nums []int) {  
    defer finished()    
    fmt.Println("Started finding largest")
    max := nums[0]
    for _, v := range nums {
        if v > max {
            max = v
        }
    }
    fmt.Println("Largest number in", nums, "is", max)
}

func main() {  
    nums := []int{78, 109, 2, 563, 300}
    largest(nums)
}           

上面是一個簡單的程式,用于查找給定的整數切片的最大值。 函數 largest() 将一個 int 切片作為參數并列印該切片的最大數字。 largest() 函數的第一行包含語句 defer finished()。 這意味着 finished() 函數将在 largest() 函數傳回之前被調用。 運作這個程式,我們可以看到下面的輸出。

Go 語言 defer(延遲) 關鍵字基本用法詳解

go defer example

largest() 函數開始執行并列印上述輸出結果的前兩行。 在它可以傳回之前,我們的 defer 函數完成執行并列印文本“Finished finding largest”:)

從上面的示例中我們可以看到,defer 語句不管是在函數中的什麼位置,都會在該函數中其他代碼執行完之後,函數傳回之前才執行。defer finished() 語句是在 largest() 函數中的最開始的位置,但是我們發現它是在最後執行的。

Go 語言 defer(延遲) 關鍵字基本用法詳解

Go defer 代碼執行順序

延遲方法

Defer 不僅限于函數。也可以在方法前面使用。我們來看一下下面這段代碼

package main

import (  
    "fmt"
)


type person struct {  
    firstName string
    lastName string
}

func (p person) fullName() {  
    fmt.Printf("%s %s",p.firstName,p.lastName)
}

func main() {  
    p := person {
        firstName: "John",
        lastName: "Smith",
    }
    defer p.fullName()
    fmt.Printf("Welcome ")  
}           

在上面的代碼中,我們在結構體 person 的方法 fullName() 前使用了defer關鍵字—— defer p.fullName()。

上面程式運作結果如下

Go 語言 defer(延遲) 關鍵字基本用法詳解

go defer method示例

參數評估

defer 函數的參數在執行 defer 語句時計算,而不是在實際函數調用完成時計算。

要怎麼了解這句話呢? 老樣子,我們還是先通過一段代碼來了解

package main

import (  
    "fmt"
)

func printA(a int) {  
    fmt.Println("value of a in deferred function", a)
}
func main() {  
    a := 5
    defer printA(a)
    a = 10
    fmt.Println("value of a before deferred function call", a)

}           

上面的代碼輸出結果如下

Go 語言 defer(延遲) 關鍵字基本用法詳解

go defer 函數參數

通過上面的代碼我們看到 defer printA(a) 在執行的時候參數 a 的值就已經确定了,而不是等到函數printA()執行的時候才去确定目前a的值。

我們通過之前的介紹知道 defer 語句不管是放在函數内部的什麼位置,由defer修飾的函數或者方法都是在包含它的函數中的其他語句執行完成之後,在函數傳回之前才會去調用。然而,雖說是最後調用,但是它的參數是早在核心執行 defer printA(a)時就已經确定了的,将當時傳參的值儲存到函數棧中了。到後面要調用的時候直接擷取的是已經儲存到棧中的值。

也就是說

func main() {  
    a := 5
    defer printA(a)
    a = 10
    fmt.Println("value of a before deferred function call", a)
}           

上面這段代碼和下面這段代碼的結果是不同的

func main() {  
    a := 5
    a = 10
    defer printA(a)
    fmt.Println("value of a before deferred function call", a)
}           

雖說 printA(a) 被調用的位置是相同的。

到這我們應該可以了解,雖然在執行 defer 語句後 a 的值變為 10,但實際的調用延遲函數 printA(a) 時仍然列印 5。

本篇我們對Go defer的基本用法進行了介紹,在下一篇 深入了解 defer關鍵字及defer實踐 中對其進入詳細介紹。敬請期待!