update tw

This commit is contained in:
chai2010
2015-12-18 10:53:03 +08:00
parent 510c741a6f
commit c66a96ee52
106 changed files with 864 additions and 864 deletions

View File

@@ -2,17 +2,17 @@
一個聲明語句將程序中的實體和一個名字關聯, 比如一個函數或一個變量. 聲明的作用域是指源代碼中可以有效使用這個名字的範圍.
不要將作用域和生命週期混一談. 聲明的作用域對應的是一個源代碼的文本區域; 它是一個編譯時的屬性. 一個變量的生命週期是程序運行時變量存在的有效時間段, 在此時間區域內存它可以被程序的其他部分引用. 是一個運行時的概唸.
不要將作用域和生命週期混一談. 聲明的作用域對應的是一個源代碼的文本區域; 它是一個編譯時的屬性. 一個變量的生命週期是程序運行時變量存在的有效時間段, 在此時間區域內存它可以被程序的其他部分引用. 是一個運行時的概唸.
語法塊是由花括弧所包含的一繫列語句, 就像函數體或循環體那樣. 語法塊內部聲明的名字是無法被外部語法塊訪問的. 語法決定了內部聲明的名字的作用域範圍. 我們可以這樣理解, 語法塊可以包含其他類似組批量聲明等沒有用花括弧包含的代碼, 我們稱之詞滙塊. 有一個語法決整個源代碼, 稱全侷塊; 然後是每個包的語法決; 每個 for, if 和 switch 語句的語法決; 每個 switch 或 select 分支的 語法決; 當然也包含顯示編寫的語法塊(花括弧包含).
語法塊是由花括弧所包含的一繫列語句, 就像函數體或循環體那樣. 語法塊內部聲明的名字是無法被外部語法塊訪問的. 語法決定了內部聲明的名字的作用域範圍. 我們可以這樣理解, 語法塊可以包含其他類似組批量聲明等沒有用花括弧包含的代碼, 我們稱之詞滙塊. 有一個語法決整個源代碼, 稱全侷塊; 然後是每個包的語法決; 每個 for, if 和 switch 語句的語法決; 每個 switch 或 select 分支的 語法決; 當然也包含顯示編寫的語法塊(花括弧包含).
聲明的詞法域決定了作用域範圍是大還是小. 內置的類型, 函數和常量, 比如 int, len 和 true 等是在全侷作用域的, 可以在整個程序中直接使用. 任何在在函數外部(也就是包級作用域)聲明的名字可以在一個包的任何Go文件訪問. 導入的包, 例如 tempconv 導入的 fmt 包, 則是對應文件級的作用域, 因此隻能在當前的文件中訪問 fmt 包, 當前包的其它文件無法訪問當前文件導入的包. 還有許多聲明, 比如 tempconv.CToF 函數中的變量 c, 則是侷部作用域的, 它隻能在函數內部(甚至隻能是某些部分)訪問.
聲明的詞法域決定了作用域範圍是大還是小. 內置的類型, 函數和常量, 比如 int, len 和 true 等是在全侷作用域的, 可以在整個程序中直接使用. 任何在在函數外部(也就是包級作用域)聲明的名字可以在一個包的任何Go文件訪問. 導入的包, 例如 tempconv 導入的 fmt 包, 則是對應文件級的作用域, 因此隻能在當前的文件中訪問 fmt 包, 當前包的其它文件無法訪問當前文件導入的包. 還有許多聲明, 比如 tempconv.CToF 函數中的變量 c, 則是侷部作用域的, 它隻能在函數內部(甚至隻能是某些部分)訪問.
控製流標簽, 例如 break, continue 或 goto 後跟着的那種標簽, 則是函數級的作用域.
控製流標簽, 例如 break, continue 或 goto 後跟着的那種標簽, 則是函數級的作用域.
一個程序可能包含多個名的聲明, 隻有它們在不的詞法域就沒有關繫. 例如, 你可以聲明一個侷部變量, 和包級的變量名. 或者是 2.3.3節的那樣, 你可以將一個函數參數的名字聲明 new, 雖然內置的new是全侷作用域的. 但是物極必反, 如果濫用重名的特性, 可能導緻程序很難閱讀.
一個程序可能包含多個名的聲明, 隻有它們在不的詞法域就沒有關繫. 例如, 你可以聲明一個侷部變量, 和包級的變量名. 或者是 2.3.3節的那樣, 你可以將一個函數參數的名字聲明 new, 雖然內置的new是全侷作用域的. 但是物極必反, 如果濫用重名的特性, 可能導緻程序很難閱讀.
當編譯器遇到一個名字引用, 它看起來像一個聲明, 它首先從最內層的詞法域全侷的作用域査找. 如果査找失敗, 則報告 "未聲明的名字" 這樣的錯誤. 如果名字在內部和外部的塊分聲明, 則內部塊的聲明首先被找到. 在這種情況下, 內部聲明屏蔽了外部名的聲明, 讓外部的聲明無法被訪問:
當編譯器遇到一個名字引用, 它看起來像一個聲明, 它首先從最內層的詞法域全侷的作用域査找. 如果査找失敗, 則報告 "未聲明的名字" 這樣的錯誤. 如果名字在內部和外部的塊分聲明, 則內部塊的聲明首先被找到. 在這種情況下, 內部聲明屏蔽了外部名的聲明, 讓外部的聲明無法被訪問:
```Go
func f() {}
@@ -27,7 +27,7 @@ func main() {
}
```
在函數中詞法域可以深度嵌套, 因此內部的一個聲明可能屏蔽外部的聲明. 還有許多塊是if或for等控製流語句構造的. 下的代碼有三個不的變量x, 因它們是定義在不的詞法域的原因. (這個例子隻是了演示作用域規則, 但不是好的編程風格.)
在函數中詞法域可以深度嵌套, 因此內部的一個聲明可能屏蔽外部的聲明. 還有許多塊是if或for等控製流語句構造的. 下的代碼有三個不的變量x, 因它們是定義在不的詞法域的原因. (這個例子隻是了演示作用域規則, 但不是好的編程風格.)
```Go
func main() {
@@ -42,11 +42,11 @@ func main() {
}
```
`x[i]``x + 'A' - 'a'` 聲明初始化的達式中都引用了外部作用域聲明的x變量, 稍後我們會解釋這個. (註意, 後麫的錶達式和unicode.ToUpper不等價.)
`x[i]``x + 'A' - 'a'` 聲明初始化的達式中都引用了外部作用域聲明的x變量, 稍後我們會解釋這個. (註意, 後面的表達式和unicode.ToUpper不等價.)
正如上所示, 不是所有的詞法域都顯示地對應到由花括弧包含的語句; 還有一些隱含的規則. 上的for語句創建了兩個詞法域: 花括弧包含的是顯式的部分是for的循環體, 另外一個隱式的部分則是循環的初始化部分, 比如用於迭代變量 i 的初始化. 隱式的部分的作用域還包含條件測試部分和循環後的迭代部分(i++), 當然也包含循環體.
正如上所示, 不是所有的詞法域都顯示地對應到由花括弧包含的語句; 還有一些隱含的規則. 上的for語句創建了兩個詞法域: 花括弧包含的是顯式的部分是for的循環體, 另外一個隱式的部分則是循環的初始化部分, 比如用於迭代變量 i 的初始化. 隱式的部分的作用域還包含條件測試部分和循環後的迭代部分(i++), 當然也包含循環體.
的例子樣有三個不的x變量, 每個聲明在不的塊, 一個在函數體塊, 一個在for語句塊, 一個在循環體塊; 隻有兩個塊是顯式創建的:
的例子樣有三個不的x變量, 每個聲明在不的塊, 一個在函數體塊, 一個在for語句塊, 一個在循環體塊; 隻有兩個塊是顯式創建的:
```Go
func main() {
@@ -58,7 +58,7 @@ func main() {
}
```
和彿如循環類似, if和switch語句也會在條件部分創建隱式塊, 還有它們對應的執行體塊. 下的 if-else 測試鏈演示的 x 和 y 的作用域範圍:
和彿如循環類似, if和switch語句也會在條件部分創建隱式塊, 還有它們對應的執行體塊. 下的 if-else 測試鏈演示的 x 和 y 的作用域範圍:
```Go
if x := f(); x == 0 {
@@ -71,9 +71,9 @@ if x := f(); x == 0 {
fmt.Println(x, y) // compile error: x and y are not visible here
```
第二個if語句嵌套在第一個內部, 因此一個if語句條件塊聲明的變量在第二個if中也可以訪問. switch語句的每個分支也有類似的規則: 條件部分一個隱式塊, 然後每個是每個分支的主體塊.
第二個if語句嵌套在第一個內部, 因此一個if語句條件塊聲明的變量在第二個if中也可以訪問. switch語句的每個分支也有類似的規則: 條件部分一個隱式塊, 然後每個是每個分支的主體塊.
在包級, 聲明的順序不會影響作用域範圍, 因此一個先聲明的可以引用它自身或者是引用後的一個聲明, 這可以讓我們定義一些相互嵌套或遞歸的類型或函數. 但是如果一個變量或常量遞歸引用了自身, 則會產生編譯錯誤.
在包級, 聲明的順序不會影響作用域範圍, 因此一個先聲明的可以引用它自身或者是引用後的一個聲明, 這可以讓我們定義一些相互嵌套或遞歸的類型或函數. 但是如果一個變量或常量遞歸引用了自身, 則會產生編譯錯誤.
在這個程序中:
@@ -85,9 +85,9 @@ f.ReadByte() // compile error: undefined f
f.Close() // compile error: undefined f
```
變量 f 的作用域隻有if語句內, 因此後的語句將無法引入它, 將導緻編譯錯誤. 你可能會收到一個侷部變量f沒有聲明的錯誤提示, 具體錯誤信息依賴編譯器的實現.
變量 f 的作用域隻有if語句內, 因此後的語句將無法引入它, 將導緻編譯錯誤. 你可能會收到一個侷部變量f沒有聲明的錯誤提示, 具體錯誤信息依賴編譯器的實現.
通常需要在if之前聲明變量, 這樣可以確保後的語句依然可以訪問變量:
通常需要在if之前聲明變量, 這樣可以確保後的語句依然可以訪問變量:
```Go
f, err := os.Open(fname)
@@ -112,7 +112,7 @@ if f, err := os.Open(fname); err != nil {
但這不是Go推薦的做法, Go的習慣是在if中處理錯誤然後直接返迴, 這樣可以確保正常成功執行的語句不需要代碼縮進.
要特註意短的變量聲明的作用域範圍, 考慮下的程序, 它的目的是取當前的工作目然後保存到一個包級的變量中. 這可以通過直接調用 os.Getwd 完成, 但是將這個從主邏輯中分離齣來可能會更好, 特是在需要處理錯誤的時候. 函數 log.Fatalf 打印信息, 然後調用 os.Exit(1) 終止程序.
要特註意短的變量聲明的作用域範圍, 考慮下的程序, 它的目的是取當前的工作目然後保存到一個包級的變量中. 這可以通過直接調用 os.Getwd 完成, 但是將這個從主邏輯中分離齣來可能會更好, 特是在需要處理錯誤的時候. 函數 log.Fatalf 打印信息, 然後調用 os.Exit(1) 終止程序.
```Go
var cwd string
@@ -125,9 +125,9 @@ func init() {
}
```
雖然cwd在外部已經聲明過, 但是 `:=` 語句還是將 cwd 和 err 重新聲明侷部變量. 內部聲明的 cwd 將屏蔽外部的聲明, 因此上的代碼不會更新包級聲明的 cwd 變量.
雖然cwd在外部已經聲明過, 但是 `:=` 語句還是將 cwd 和 err 重新聲明侷部變量. 內部聲明的 cwd 將屏蔽外部的聲明, 因此上的代碼不會更新包級聲明的 cwd 變量.
當前的編譯器將檢測到侷部聲明的cwd沒有本使用, 然後報告這可能是一個錯誤, 但是這種檢測不可靠. 一些小的代碼變更, 例如增加一個侷部cwd的打印語句, 就可能導緻這種檢測失效.
當前的編譯器將檢測到侷部聲明的cwd沒有本使用, 然後報告這可能是一個錯誤, 但是這種檢測不可靠. 一些小的代碼變更, 例如增加一個侷部cwd的打印語句, 就可能導緻這種檢測失效.
```Go
var cwd string
@@ -157,6 +157,6 @@ func init() {
}
```
我們已經看到包, 文件, 聲明和語句如何來達一個程序結構. 在下的兩個章節, 我們將探討數據的結構.
我們已經看到包, 文件, 聲明和語句如何來達一個程序結構. 在下的兩個章節, 我們將探討數據的結構.
**譯註: 本章的詞法域和作用域概唸有些混淆, 需要重譯一遍.**