Go 語言中的一等公民:看似普通的函數,憑什麼?

如有任何問題或建議,歡迎及時交流和碰撞。個人公衆號是 【腦子進煎魚了】,GitHub 地址: https://github.com/eddycjy

你們好,我是煎魚。git

在 Go 語言中,一提函數,你們提的最多的就是 「Go 語言的函數是一等公民」。這個定義來的很是忽然,咱們先了解一下什麼是一等公民。github

根據維基百科的一等公民(First-class citizen)的定義:編程

In programming language design, a first-class citizen (also type, object, entity, or value) in a given programming language is an entity which supports all the operations generally available to other entities. These operations typically include being passed as an argument, returned from a function, modified, and assigned to a variable.

在編程語言設計中,給定編程語言中的一等公民(也就是類型,對象,實體或值)能夠把函數賦值給變量,也能夠把函數做爲其它函數的參數或者返回值來直接使用。數組

Go 語言的函數也知足這個定義,所以常被稱爲 「一等公民」。瞭解清楚背景後,接下來進一步展開。架構

普通函數

在 Go 語言中普通函數的定義格式爲 func [函數名](入參)(出參),以下:app

func callFuncA(x, y string) (s string, err error) {
    return x + y, nil
}

func main() {
    callFuncA("炸", "煎魚")
}

在示例代碼中聲明瞭一個函數名爲 callFuncA 的方法,他只容許在包內調用,所以首字母爲小寫。編程語言

其具備兩個入參,分別是 xy,類型都爲 string。而出參爲變量 serr,類型分別爲 stringerror函數

另外在函數體內返回值時,也能夠採用快捷返回的方式:微服務

func callFuncA(x, y string) (s string, err error) {
    s = x + y
    return
}

在出參時所聲明的變量名稱,是能夠應用到自身函數的。所以若直接執行 return 則會隱式返回已經聲明的出參變量。spa

在函數定義時,其入參還支持可變參數的語法:

func callFuncA(x ...string) (s string, err error) {
    s = strings.Join(x, ",")
    return
}

func main() {
    fmt.Println(callFuncA("炸", "煎魚"))
}

在入參變量上聲明爲 x ...string,則表示變量 xstring 類型的可變變量,可以在入參時傳入多個 string 參數。

可變變量所傳入的格式爲切片(slice)類型,該類型咱們會在後面的章節進行講解,你能夠理解爲不受長度限制的動態數組:

[0: 炸 1: 煎魚]

通常對可變變量的常見後續操做可能是循環遍歷處理,又或是進行拼接等操做。

匿名函數

Go 語言也默認支持匿名函數的聲明,聲明的方式與普通函數幾乎同樣:

func main() {
    s := func(x, y string) (s string, err error) {
        return x + y, nil
    }

    s("炸", "煎魚")
}

匿名函數能夠在任意地方聲明,且不須要定義函數名,若是在函數體後立刻跟 () 則表示聲明後當即執行:

func main() {
    s, _ := func(x, y string) (s string, err error) {
        return x + y, nil
    }("炸", "煎魚")
}

而在全部的函數類使用中,有一點很是重要,那就是函數變量做用域的理解:

func main() {
    x, y := "炸", "煎魚"
    s, _ := func() (s string, err error) {
        return x + y, nil
    }()
    fmt.Println(s)
}

該匿名函數沒有形參,函數內部沒有定義相應的變量,此時其讀取的是全局的 xy 變量的值,輸出結果是 「炸煎魚」。

func main() {
    x, y := "炸", "煎魚"
    _, _ = func(x, y string) (s string, err error) {
        x = "吃"
        return x + y, nil
    }(x, y)
    fmt.Println(x, y)
}

該匿名函數有形參,可是在函數內部又從新賦值了變量 x。那麼最終外部所輸出的變量 x 的值是什麼呢?輸出結果是 「炸 煎魚」。

爲何明明在函數內已經對變量 x 從新賦值,卻依然沒有改變全局變量 x 的值呢?

其本質緣由是做用域不一樣,函數內部所修改的變量 x 是函數內的局部變量。而外部的是全局的變量,所歸屬的做用域不一樣。

結構方法

在結合結構體(struct)的方式下,能夠聲明歸屬於該結構體下的方法:

type T struct{}

func NewT() *T {
    return &T{}
}

func (t *T) callFuncA(x, y string) (s string, err error) {
    return x + y, nil
}

func main() {
    NewT().callFuncA("炸", "煎魚")
}

具體的函數的使用方法與普通函數同樣,無其餘區別。

而與結構體有關的值傳遞、引用傳遞的方法調用將在具體後面的章節再展開。

內置函數

Go 語言自己有支持一些內置函數,這些內置函數的調用不須要引用第三方標準庫。內置函數的做用是用於配合 Go 語言的常規使用,數量很是少。以下:

  • 用於獲取某些類型的長度和容量:len、cap。
  • 用於建立並分配某些類型的內存:new、make。
  • 用於錯誤處理機制(異常恐慌、異常捕獲):panic、recover。
  • 用於複製和新增切片(slice):copy、append。
  • 用於簡單輸出信息:print、println。
  • 用於處理複數:complex、real、imag。

針對每一個內置函數的真實使用場景,咱們會在後續的章節再進一步展開,由於每一個內置函數本質上都對應着各種型的使用場景。

總結

在本章節中,咱們介紹了 Go 語言的函數爲何稱是一等公民,而且針對函數的各種變形:普通函數、匿名函數、結構方法、內置函數進行了基本的說明。

面對新手入門最容易犯錯的函數做用域問題,也進行了基本的梳理。這塊建議你們要多多深刻思考、理解,避免往後踩坑。

個人公衆號

分享 Go 語言、微服務架構和奇怪的系統設計,歡迎你們關注個人公衆號和我進行交流和溝通。

最好的關係是互相成就,各位的點贊就是煎魚創做的最大動力,感謝支持。

相關文章
相關標籤/搜索