18. 理解Go 語言中的 語句塊與做用域

Hi,你們好,我是明哥。python

在本身學習 Golang 的這段時間裏,我寫了詳細的學習筆記放在個人我的微信公衆號 《Go編程時光》,對於 Go 語言,我也算是個初學者,所以寫的東西應該會比較適合剛接觸的同窗,若是你也是剛學習 Go 語言,不防關注一下,一塊兒學習,一塊兒成長。git

個人在線博客:http://golang.iswbm.com
個人 Github:github.com/iswbm/GolangCodingTimegithub


因爲 Go 使用的是詞法做用域,而詞法做用域依賴於語句塊。因此在講做用域時,須要先了解一下 Go 中的語句塊是怎麼一回事?golang

1. 顯示語句塊與隱式語句塊

通俗地說,語句塊是由花括弧({})所包含的一系列語句。shell

語句塊內部聲明的名字是沒法被外部塊訪問的。這個塊決定了內部聲明的名字的做用域範圍,也就是做用域。編程

用花括弧包含的語句塊,屬於顯示語句塊。數組

在 Go 中還有不少的隱式語句塊:bash

  • 主語句塊:包括全部源碼,對應內置做用域
  • 包語句塊:包括該包中全部的源碼(一個包可能會包括一個目錄下的多個文件),對應包級做用域
  • 文件語句塊:包括該文件中的全部源碼,對應文件級做用域
  • for 、if、switch等語句自己也在它自身的隱式語句塊中,對應局部做用域

前面三點好理解,第四點舉幾個例子微信

for 循環完後,不能再使用變量 i編程語言

for i := 0; i < 5; i++ {
    fmt.Println(i)
}

if 語句判斷完後,一樣不能再使用變量 i

if i := 0; i >= 0 {
    fmt.Println(i)
}

switch 語句完了後,也是否是再使用變量 i

switch i := 2; i * 4 {
case 8:
    fmt.Println(i)
default:
    fmt.Println(「default」)
}

且每一個 switch 語句的子句都是一個隱式的語句塊

switch i := 2; i * 4 {
case 8:
    j := 0
    fmt.Println(i, j)
default:
    // "j" is undefined here
    fmt.Println(「default」)
}
// "j" is undefined here

2. 四種做用域的理解

變量的聲明,除了聲明其類型,其聲明的位置也有講究,不一樣的位置決定了其擁有不一樣的做用範圍,說白了就是我這個變量,在哪裏可用,在哪裏不可用。

根據聲明位置的不一樣,做用域能夠分爲如下四個類型:

  • 內置做用域:不須要本身聲明,全部的關鍵字和內置類型、函數都擁有全局做用域
  • 包級做用域:必須函數外聲明,在該包內的全部文件均可以訪問
  • 文件級做用域:不須要聲明,導入便可。一個文件中經過import導入的包名,只在該文件內可用
  • 局部做用域:在本身的語句塊內聲明,包括函數,for、if 等語句塊,或自定義的 {} 語句塊造成的做用域,只在本身的局部做用域內可用

以上的四種做用域,從上往下,範圍從大到小,爲了表述方便,我這裏本身將範圍大的做用域稱爲高層做用域,而範圍小的稱爲低層做用域。

對於做用域,有如下幾點總結:

  • 低層做用域,能夠訪問高層做用域
  • 同一層級的做用域,是相互隔離的
  • 低層做用域裏聲明的變量,會覆蓋高層做用域裏聲明的變量

在這裏要注意一下,不要將做用域和生命週期混爲一談。聲明語句的做用域對應的是一個源代碼的文本區域;它是一個編譯時的屬性。

而一個變量的生命週期是指程序運行時變量存在的有效時間段,在此時間區域內它能夠被程序的其餘部分引用;是一個運行時的概念。

3. 靜態做用域與動態做用域

根據局部做用域內變量的可見性,是不是靜態不變,能夠將編程語言分爲以下兩種:

  • 靜態做用域,如 Go 語言
  • 動態做用域,如 Shell 語言

具體什麼是動態做用域,這裏用 Shell 的代碼演示一下,你就知道了

#!/bin/bash
func01() {
    local value=1
    func02
}
func02() {
    echo "func02 sees value as ${value}"
}

# 執行函數
func01
func02

從代碼中,能夠看到在 func01 函數中定義了個局部變量 value,按理說,這個 value 變量只在該函數內可用,但因爲在 shell 中的做用域是動態的,因此在 func01中也能夠調用 func02 時,func02 能夠訪問到 value 變量,此時的 func02 做用域能夠當成是 局部做用域中(func01)的局部做用域。

但若脫離了 func01的執行環境,將其放在全局環境下或者其餘函數中, func02 是訪問不了 value 變量的。

因此此時的輸出結果是

func02 sees value as 1
func02 sees value as

但在 Go 中並不存在這種動態做用域,好比這段代碼,在func01函數中,要想取得 name 這個變量,只能從func01的做用域或者更高層做用域裏查找(文件級做用域,包級做用域和內置做用域),而不能從調用它的另外一個局部做用域中(由於他們在層級上屬於同一級)查找。

import "fmt"

func func01() {
	fmt.Println("在 func01 函數中,name:", name)
}

func main()  {
	var name string = "Python編程時光"
	fmt.Println("在 main 函數中,name:", name)

	func01()
}

所以你在執行這段代碼時,會報錯,提示在func01中的name還未定義。

參考文章:https://studygolang.com/articles/12632

系列導讀

01. 開發環境的搭建(Goland & VS Code)

02. 學習五種變量建立的方法

03. 詳解數據類型:****整形與浮點型

04. 詳解數據類型:byte、rune與string

05. 詳解數據類型:數組與切片

06. 詳解數據類型:字典與布爾類型

07. 詳解數據類型:指針

08. 面向對象編程:結構體與繼承

09. 一篇文章理解 Go 裏的函數

10. Go語言流程控制:if-else 條件語句

11. Go語言流程控制:switch-case 選擇語句

12. Go語言流程控制:for 循環語句

13. Go語言流程控制:goto 無條件跳轉

14. Go語言流程控制:defer 延遲調用

15. 面向對象編程:接口與多態

16. 關鍵字:make 和 new 的區別?

17. 一篇文章理解 Go 裏的語句塊與做用域

18. 學習 Go 協程:goroutine

19. 學習 Go 協程:詳解信道/通道

20. 幾個信道死鎖經典錯誤案例詳解

21. 學習 Go 協程:WaitGroup

22. 學習 Go 協程:互斥鎖和讀寫鎖

23. Go 裏的異常處理:panic 和 recover

24. 超詳細解讀 Go Modules 前世此生及入門使用

25. Go 語言中關於包導入必學的 8 個知識點

26. 如何開源本身寫的模塊給別人用?

27. 說說 Go 語言中的類型斷言?

28. 這五點帶你理解Go語言的select用法


相關文章
相關標籤/搜索