一篇文章帶你了解Go語(yǔ)言基礎(chǔ)之函數(shù)(中篇)
前言
Hey,大家好呀,我是星期八,上篇文章學(xué)了些基礎(chǔ):一篇文章帶你了解Go語(yǔ)言基礎(chǔ)之函數(shù)(上篇),這次咱們繼續(xù)學(xué)習(xí)Go基礎(chǔ)之函數(shù)進(jìn)階叭。
Go函數(shù)內(nèi)存分配圖
Go的函數(shù)內(nèi)存分配,有點(diǎn)像堆分配,有點(diǎn)像,但是本質(zhì)不是。
可以理解像堆內(nèi)存一樣,棧中保存的是堆的地址。
驗(yàn)證
代碼
- package main
- import "fmt"
- func say() string {
- return "ok"
- }
- func main() {
- fmt.Printf("say棧上的內(nèi)容:%p\n",say)
- }
結(jié)果
本質(zhì)
函數(shù)的作用域
作用域這個(gè)問(wèn)題,以前可能或多或少提過(guò),再來(lái)復(fù)習(xí)一下叭。
全局變量
全局變量就是在所有函數(shù)外部定義的變量,程序不結(jié)束,變量就一直存在。
當(dāng)然,任何函數(shù)都可以訪問(wèn)全局變量。
注:全局變量盡量全部用大寫(xiě)。
小試牛刀
- package main
- import "fmt"
- var NAME = "張三"
- func say() string {
- fmt.Println(NAME)
- return "ok"
- }
- func main() {
- say()
- fmt.Println(NAME)
- }
結(jié)果:
上述可能會(huì)有個(gè)問(wèn)題,全局變量,全局變量,大家共用一個(gè),要是誰(shuí)傻不拉幾修改了不就完蛋了,整個(gè)程序都涼了。
var引發(fā)的問(wèn)題
就像這樣。
- package main
- import "fmt"
- var NAME = "張三"
- func say() string {
- fmt.Println(NAME)
- NAME = "李四"
- return "ok"
- }
- func main() {
- say()
- fmt.Println(NAME)
- }
結(jié)果:
這不就完?duì)僮恿藛???所以,一定要有解決辦法。
使用const解決問(wèn)題
解決辦法:使用常量定義全局變量。
- package main
- import "fmt"
- const NAME = "張三"
- func say() string {
- fmt.Println(NAME)
- //NAME = "李四"//會(huì)報(bào)錯(cuò):cannot assign to NAME
- return "ok"
- }
- func main() {
- say()
- fmt.Println(NAME)
- }
總結(jié)
在定義全局變量時(shí),需要用const修飾,并且變量名全部大寫(xiě)。
局部變量
局部變量,局部變量就是在某個(gè)函數(shù)內(nèi)定義的變量,只能在自己函數(shù)內(nèi)使用。
更專(zhuān)業(yè)點(diǎn),在{}內(nèi)定義的,只能在{}內(nèi)使用,for同理。
代碼
- package main
- import (
- "fmt"
- )
- func say() string {
- var name = "張三"
- fmt.Println(name)
- return "ok"
- }
- func main() {
- say()
- //fmt.Println(name)//會(huì)報(bào)錯(cuò):undefined: name
- //for同理
- for i := 0; i <= 1; i++ {
- var c = "66"
- fmt.Println(c) //66
- }
- //fmt.Println(c)//會(huì)報(bào)錯(cuò):undefined: c
- }
defer
在Go中,defer語(yǔ)句,可以理解為在return之前執(zhí)行的一個(gè)語(yǔ)句。
如果函數(shù)沒(méi)有return,會(huì)有一個(gè)默認(rèn)的return,只是看不見(jiàn)而已。
一個(gè)defer
代碼
- package main
- import "fmt"
- func say() {
- //defer盡量往前放
- defer fmt.Println("我是666")
- fmt.Println("你們都是最棒的")
- }
- func main() {
- say()
- }
執(zhí)行結(jié)果
多個(gè)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()
- }
執(zhí)行結(jié)果
可以發(fā)現(xiàn),defer的執(zhí)行結(jié)果是反著的。
結(jié)論:最先執(zhí)行的defer,會(huì)最后執(zhí)行,最后執(zhí)行的defer,會(huì)最先執(zhí)行,有點(diǎn)像棧,先進(jìn)后出。
defer的作用
通常來(lái)說(shuō),defer會(huì)用在釋放數(shù)據(jù)庫(kù)連接,關(guān)閉文件等需要在函數(shù)結(jié)束時(shí)處理的操作。
這里暫時(shí)先不舉例子。
panic和recover
這倆,可以理解為Python中的try和raise,因?yàn)樵贕o中,是沒(méi)有try的,是不能像其他語(yǔ)言一樣,try所有異常。
應(yīng)用場(chǎng)景:比如某個(gè)web,在啟動(dòng)時(shí),數(shù)據(jù)庫(kù)都沒(méi)連接成功,必定要啟動(dòng)失敗,就像電腦,沒(méi)有電源必不能開(kāi)機(jī)一樣。
panic
先看一下語(yǔ)法吧
- package main
- import "fmt"
- func say() {
- var flag = true
- if flag{
- //引發(fā)錯(cuò)誤,直接中斷程序的錯(cuò)誤
- panic("OMG,撤了撤了,必須撤了")
- }
- }
- func main() {
- say()
- fmt.Println("繼續(xù)呀...")//不會(huì)執(zhí)行,程序掛了
- }
執(zhí)行效果
可以看淡,繼續(xù)呀就沒(méi)打印,程序直接掛了,但是上述好像并沒(méi)有解決這個(gè)問(wèn)題。
recover
嘗試捕捉
代碼
- package main
- import "fmt"
- func say() {
- //匿名函數(shù),defer執(zhí)行的是一個(gè)匿名函數(shù)
- defer func() {
- var err = recover()
- //如果有panic錯(cuò)誤,err!=nil,在此處步驟,嘗試恢復(fù)
- if err != nil {
- fmt.Println("嘗試恢復(fù)...")
- }
- }()
- var flag = true
- if flag {
- panic("OMG,撤了撤了,必須撤了")
- }
- }
- func main() {
- say()
- fmt.Println("繼續(xù)呀...")
- }
執(zhí)行結(jié)果
可以看到,如果recover捕捉了,并且沒(méi)有panic,程序就會(huì)繼續(xù)正常執(zhí)行。
注意
defer必須在panic語(yǔ)句之前。
recover必須配合defer使用。
總結(jié)
上述我們學(xué)習(xí)了Go基礎(chǔ)之函數(shù)進(jìn)階。如果在操作過(guò)程中有任何問(wèn)題,記得下面討論區(qū)留言,我們看到會(huì)第一時(shí)間解決問(wèn)題。
本文轉(zhuǎn)載自微信公眾號(hào)「Go語(yǔ)言進(jìn)階學(xué)習(xí)」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系Go語(yǔ)言進(jìn)階學(xué)習(xí)公眾號(hào)。






























