前言
如果不深入理解 Go 語(yǔ)言的代碼塊作用域,程序?qū)a(chǎn)生我們無(wú)法理解的行為,比如說(shuō)在循環(huán)中創(chuàng)建 goroutine func, 為什么需要傳遞參數(shù)至 goroutine 內(nèi)部,否則所有的 func 使用的變量參數(shù)都是循環(huán)的最后一個(gè)值。
看下邊這個(gè) demo, 就需要深入理解 Go 語(yǔ)言代碼塊的作用域才能理直氣壯的一口答對(duì):
func main() {
if a := 1; false {
} else if b := 2; false {
} else if c := 3; false {
} else {
println(a, b, c)
}
}
有兩個(gè)答案選項(xiàng):
A:1 2 3
B:無(wú)法通過(guò)編譯
思考三秒鐘??-----------------
正確答案是 A,你答對(duì)了嗎,接下來(lái)將圍繞上述例子來(lái)理解一下Go代碼塊(code block)和作用域(scope)規(guī)則,理解這些規(guī)則將有助于我們編寫出正確且可讀性高的代碼。
代碼塊與作用域簡(jiǎn)介
Go語(yǔ)言中的代碼塊是包裹在一對(duì)大括號(hào)內(nèi)部的聲明和語(yǔ)句,且代碼塊支持嵌套。如果一對(duì)大括號(hào)之間沒有任何語(yǔ)句,那么稱這個(gè)代碼塊為空代碼塊。代碼塊是代碼執(zhí)行流流轉(zhuǎn)的基本單元,代碼執(zhí)行流總是從一個(gè)代碼塊跳到另一個(gè)代碼塊。
顯示代碼塊
- 由大括號(hào)包裹,比如函數(shù)體、for 循環(huán)的循環(huán)體、if 語(yǔ)句的某個(gè)分支等。
隱式代碼塊
- 宇宙代碼塊: 所有Go源碼都在該隱式代碼塊中,就相當(dāng)于所有Go代碼的最外層都存在一對(duì)大括號(hào)。
- 包代碼塊: 每個(gè)包都有一個(gè)包代碼塊,其中放置著該包的所有Go源碼。
- 文件代碼塊:每個(gè)文件都有一個(gè)文件代碼塊,其中包含著該文件中的所有Go源碼。
- 每個(gè)if、for和switch語(yǔ)句均被視為位于其自己的隱式代碼塊中。
- switch或select語(yǔ)句中的每個(gè)子句都被視為一個(gè)隱式代碼塊。
Go標(biāo)識(shí)符的作用域是基于代碼塊定義的,作用域規(guī)則描述了標(biāo)識(shí)符在哪些代碼塊中是有效的。下面是標(biāo)識(shí)符作用域規(guī)則。
在函數(shù)內(nèi)部聲明的類型標(biāo)識(shí)符的作用域范圍始于類型定義中的標(biāo)識(shí)符,止于其最里面的那個(gè)包含塊的末尾:
if 條件控制語(yǔ)句的代碼塊
-
單 if 型
同時(shí)包含一個(gè)隱式代碼塊和一個(gè)顯式代碼塊:
func Foo() { if a := 1; true { fmt.Println(a) } } // 等價(jià)變換為 func Foo() { { a := 1 if true { fmt.Println(a) } } }
-
if {} else {} 型
包含三個(gè)代碼塊,單 if 的兩個(gè)代碼塊,else 的顯式代碼塊:
func Foo() { if a,b := 1, 2; false { fmt.Println(a) } else { fmt.Println(b) } } // 等價(jià)變換為 func Foo() { { a, b := 1, 2 if false { fmt.Println(a) } else { fmt.Println(b) } } }
-
if {} else if {} else {} 型
**對(duì)上邊的偽代碼進(jìn)行變換:
此時(shí)就可以對(duì)一開始的答案進(jìn)行解釋了:func main() { if a := 1; false { } else if b := 2; false { } else if c := 3; false { } else { println(a, b, c) } } // 進(jìn)行等價(jià)變換后 func main() { { a := 1 if false { } else { { b := 2 if false { } else { { c := 3 if false { } else { println(a, b, c) } } } } } } }
其他控制語(yǔ)句的代碼塊
-
for 語(yǔ)句的代碼塊
通用 for 控制語(yǔ)句等價(jià)變換:for a, b := 1, 10; a < b; a++ { ... } // 等價(jià)轉(zhuǎn)換為 { a, b := 1, 10 for ; a < b; a++ { ... } }
for range 語(yǔ)句等價(jià)變換:
var sl = []int{1, 2, 3} for i, n := range sl { ... } // 等價(jià)變換為 var sl = []int{1, 2, 3} { i, n := 0, 0 for i, n := range sl { ... } }
-
switch-case 語(yǔ)句的代碼塊
通用形式等價(jià)變換:switch x, y := 1, 2; x + y { case 3: a := 1 fmt.Println("case1: a = ", a) fallthrough case 10: a := 5 fmt.Println("case2: a = ", a) fallthrough default: a := 7 fmt.Println("default case: a = ", a) } // 等價(jià)變換為 { x, y := 1, 2 switch x + y { case 3: { a := 1 fmt.Println("case1: a = ", a) } fallthrough case 10: { a := 5 fmt.Println("case2: a = ", a) } fallthrough default: { a := 7 fmt.Println("default case: a = ", a) } } }
-
select-case 語(yǔ)句的代碼塊
通用形式等價(jià)變換:
c1 := make(chan int) c2 := make(chan int, 1) c2 <- 11 select { case c1 <- 1: fmt.Println("SendStmt case has been chosen") case i := <-c2: _ = i fmt.Println("RecvStmt case has been chosen") default: fmt.Println("default case has been chosen") } // 等價(jià)變換為 (偽代碼) c1 := make(chan int) c2 := make(chan int, 1) c2 <- 11 select { case c1 <- 1: { fmt.Println("SendStmt case has been chosen") } case "如果該case 被選擇": { i := <-c2: _ = i fmt.Println("RecvStmt case has been chosen") } default: { fmt.Println("default case has been chosen") } }
總結(jié)
各類隱式代碼塊的規(guī)則才是理解Go代碼塊和作用域的規(guī)則的“金鑰匙”,尤其是那些對(duì)于程序執(zhí)行流有重大影響的控制語(yǔ)句的隱式代碼塊規(guī)則。
理解Go代碼塊和作用域的規(guī)則將有助于我們快速解決類似“變量未定義”的錯(cuò)誤和上一層變量被內(nèi)層同名變量遮蔽(shadow)的問(wèn)題,同時(shí)對(duì)于正確理解Go程序的執(zhí)行流也大有裨益。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-510133.html
文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-510133.html
到了這里,關(guān)于Go語(yǔ)言代碼塊與作用域理解的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!