iT邦幫忙

2023 iThome 鐵人賽

DAY 14
0
Modern Web

就是個Go,我也可以啦!GOGO系列 第 14

2023鐵人賽Day 14 Go 哪兒?變數作用域在哪裡

  • 分享至 

  • xImage
  •  

什麼是作用域

作用域敘述了變數及function的可見性和生存週期,可以簡單來說,我在意的是code的哪些部分可以訪問特定的變數,所以我們會去學習這個語言的區域作用域,全域作用域,go有個很經典的問題

package main

import (
	"fmt"
)

func main() {
	if a := 1; false {
	} else if b := 2; false {
	} else if c := 3; false {
	} else {
		fmt.Println(a, b, c)
	}
}

以上答案會是什麼
我們先不解答然後,先往下認識

Go 語言中的代碼塊(code block)

代碼塊是由一對大括號 {} 包裹起來的一系列語句。在 Go 中,代碼塊用於定義一系列語句的邏輯邊界

顯式及隱式作用域

  • 顯式
    • 函數的函數體
    • for 循環的循環體
    • if, else if, 和 else 語句的條件體
    • switch 和 select 語句
  • 隱式: 這些代碼塊不由大括號直接定義,但它們在語義上存在
    • 宇宙作用域 (Universe Block):所有 Go 程序的最外層作用域
    • 包作用域 (Package Block):定義在一個特定包中的所有文件共享的作用域
    • 文件作用域 (File Block):一個文件內的頂級聲明共享的作用域。
    • case 或 default 子句在 switch 和 select 語句中

作用域的規則

  1. 宇宙塊(Universe Block)

    • make, new, cap, 和 len 這些關鍵字是 Go 語言的內建函數,它們在任何地方都可以訪問。它們是在宇宙作用域(或稱宇宙塊)中定義的。
  2. 包作用域(Package Block)

    • 當你在一個文件的頂部(即任何函數外)聲明常量、類型、變數或函數(但不包括方法)時,這些聲明的作用域是整個包。也就是說,同一個包的其他文件也可以訪問這些聲明。
  3. 文件作用域(File Block)

    • 當你在一個 Go 文件中使用 import 語句導入其他包時,那個包的名稱的作用域僅限於該文件。其他文件,即使它們是同一個包的,也不能直接使用那個導入的包名稱,除非它們也導入了相同的包。
  4. 區域/局部作用域

    • 在函數內部聲明的常量和變數的作用域從它們被聲明的地方開始,並終止於包含它們的最內層的代碼塊結束的地方。
    • 在函數內部聲明的類型(例如在某個特定函數中聲明的自定義結構或接口)也遵循相同的規則。它的作用域從類型的定義開始,到最內層的代碼塊結束。
      func exampleFunction() {
          x := 10 // x的作用域從這裡開始
    
          if x > 5 {
              y := 5 // y的作用域只在這個if語句的大括號內
              fmt.Println(y) // 這裡可以訪問y
          }
    
          // fmt.Println(y) // 這裡會報錯,因為y在這裡是不可見的
    
          fmt.Println(x) // 這裡可以訪問x
      } // x和y的作用域在這裡結束
    
    

if 條件的block

在 Go 語言中,if 語句(以及其他控制結構,如 for 和 switch)都在隱式的代碼塊中,這意味著你可以在 if 語句中有其自己的變數聲明和作用域

單if型

if a:= 1; a > 1 {
  ...
}

根據上述的隱式的代碼塊原則,我們可以把這段程式碼等價於

{ // 隱式block開始
  a := 1
  
  if a > 1 { // 顯式block開始
    
  } // 顯式block結束
} // 隱式block結束

這也可以說明為什麼 a在外面聲明但可以在if裡面使用

單if-else型

if a := 1; a > 1 {
    ...
} else {
    ...
}

上面等價於

{ // 隱式block開始
    a := 1
    
    if a > 1 { // 顯式block開始
        ...
    } else { // 另一個顯式block開始
        ...
    } // 顯式blocks結束
} // 隱式block結束

上面的a, 可以在其後面的else語句中使用。因為整個if-else結構都在一個隱式的外部代碼塊中,所以在這個隱式代碼塊中聲明的變數在整個if-else結構中都是可見的

if-else if-else 型

if a := 1; a > 1 {
    ...
} else if b := 2; b > 2 {
    ...
} else {
    ...
}

上面等價於

{ // 外部隱式block開始
    a := 1
    
    if a > 1 { // 第一個顯式block開始
        ...
    } else { // 第二個隱式block開始,為了包含 else if 和可能的後續 else
        b := 2
        
        if b > 2 { // 第二個顯式block開始
            ...
        } else { // 第三個顯式block開始
            ...
        } // 第三個顯式block結束
    } // 第二個隱式block結束
} // 外部隱式block結束

如果你在第一個 if 的條件中聲明了一個變數,那麼你可以在 else if 或 else 部分中訪問它。同樣,如果你在 else if 的條件部分中聲明了一個變數,那麼你可以在其後面的 else 部分中訪問它

依照這個規則你應該就有辦法推出第一題的答案了吧

for 語句

for a, b := 1, 10 ; a < b ; a++ {
  ...
}

上面的code可以轉為

{
  a, b := 1 
  for ; a< b; a++ {
    ...
  }
}

當你做了以上的轉換,你就可以很清楚的知道你定義的這些變數可以在哪裡使用,哪裡可見,希望對你有幫助


上一篇
2023鐵人賽Day 13 Go 的 iota:建立靈活的列舉系統
下一篇
2023鐵人賽Day 15 Go 之道:從 if 的快樂路徑到 for range 的安全遊走
系列文
就是個Go,我也可以啦!GOGO30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言