一篇文章帶你瞭解 Go 語言基礎之函數(中篇)

前言

Hey,大家好呀,我是星期八,上篇文章學了些基礎:一篇文章帶你瞭解 Go 語言基礎之函數(上篇),這次咱們繼續學習 Go 基礎之函數進階叭。

Go 函數內存分配圖

Go 的函數內存分配,有點像堆分配,有點像,但是本質不是。

可以理解像堆內存一樣,棧中保存的是堆的地址。

驗證

代碼

package main
import "fmt"
func say() string {
    return "ok"
}
func main() {
    fmt.Printf("say棧上的內容:%p\n"say)
}

結果

本質

函數的作用域


作用域這個問題,以前可能或多或少提過,再來複習一下叭。

全局變量

全局變量就是在所有函數外部定義的變量,程序不結束,變量就一直存在。

當然,任何函數都可以訪問全局變量。

注: 全局變量儘量全部用大寫。

小試牛刀

package main
import "fmt"
var NAME = "張三"
func say() string {
    fmt.Println(NAME)
    return "ok"
}
func main() {
    say()
    fmt.Println(NAME)
}

結果:

上述可能會有個問題,全局變量,全局變量,大家共用一個,要是誰傻不拉幾修改了不就完蛋了,整個程序都涼了。

var 引發的問題

就像這樣。

package main
import "fmt"
var NAME = "張三"
func say() string {
    fmt.Println(NAME)
    NAME = "李四"
    return "ok"
}
func main() {
    say()
    fmt.Println(NAME)
}

結果:

這不就完犢子了嗎??? 所以,一定要有解決辦法。

使用 const 解決問題

解決辦法: 使用常量定義全局變量。

package main
import "fmt"
const NAME = "張三"
func say() string {
    fmt.Println(NAME)
    //NAME = "李四"//會報錯:cannot assign to NAME
    return "ok"
}
func main() {
    say()
    fmt.Println(NAME)
}

總結

在定義全局變量時,需要用const修飾,並且變量名全部大寫。

局部變量

局部變量,局部變量就是在某個函數內定義的變量,只能在自己函數內使用。

更專業點,在{}內定義的,只能在{}內使用,for同理。

代碼

package main
import (
    "fmt"
)
func say() string {
    var name = "張三"
    fmt.Println(name)
    return "ok"
}
func main() {
    say()
    //fmt.Println(name)//會報錯:undefined: name
    //for同理
    for i := 0; i <= 1; i++ {
        var c = "66"
        fmt.Println(c) //66
}
    //fmt.Println(c)//會報錯:undefined: c
}

defer


在 Go 中,defer語句,可以理解爲在return之前執行的一個語句。

如果函數沒有return,會有一個默認的return,只是看不見而已。

一個 defer

代碼

package main
import "fmt"
func say() {
    //defer儘量往前放
    defer fmt.Println("我是666")
    fmt.Println("你們都是最棒的")
}
func main() {
    say()
}

執行結果

多個 defer

代碼

package main
import "fmt"
func say() {
    //defer儘量往前放
    defer fmt.Println(1)
    defer fmt.Println(2)
    defer fmt.Println(3)
    fmt.Println("你們都是最棒的")
}
func main() {
    say()
}

執行結果

可以發現,defer 的執行結果是反着的。

結論:先執行的 defer,會最後執行,最後執行的 defer,會最先執行,有點像棧,先進後出

defer 的作用

通常來說,defer 會用在釋放數據庫連接,關閉文件等需要在函數結束時處理的操作。

這裏暫時先不舉例子。

panic 和 recover

這倆,可以理解爲 Python 中的tryraise,因爲在 Go 中,是沒有try的,是不能像其他語言一樣,try所有異常。

應用場景: 比如某個 web,在啓動時,數據庫都沒連接成功,必定要啓動失敗,就像電腦,沒有電源必不能開機一樣。

panic

先看一下語法吧

package main
import "fmt"
func say() {
    var flag = true
    if flag{
        //引發錯誤,直接中斷程序的錯誤
        panic("OMG,撤了撤了,必須撤了")
}
}
func main() {
    say()
    fmt.Println("繼續呀...")//不會執行,程序掛了
}

執行效果

可以看淡,繼續呀就沒打印,程序直接掛了,但是上述好像並沒有解決這個問題。

recover

嘗試捕捉

代碼

package main
import "fmt"
func say() {
  //匿名函數,defer執行的是一個匿名函數
  defer func() {
    var err = recover()
    //如果有panic錯誤,err!=nil,在此處步驟,嘗試恢復
    if err != nil {
      fmt.Println("嘗試恢復...")
    }
  }()
  var flag = true
  if flag {
    panic("OMG,撤了撤了,必須撤了")
  }
}
func main() {
  say()
  fmt.Println("繼續呀...")
}

執行結果

可以看到,如果 recover 捕捉了,並且沒有panic,程序就會繼續正常執行。

注意

defer必須在panic語句之前。

recover必須配合defer使用。

總結

上述我們學習了 Go 基礎之函數進階。如果在操作過程中有任何問題,記得下面討論區留言,我們看到會第一時間解決問題。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/obQsFaTvrqijbUEOteVmQA