回到简体

This commit is contained in:
chai2010
2016-02-15 11:06:34 +08:00
parent 9e878f9944
commit 2b37b23285
177 changed files with 2354 additions and 2354 deletions

View File

@@ -1,18 +1,18 @@
## 2.7. 作用域
個聲明語句將程序中的實體和一名字關聯,比如一個函數或一個變量。聲明語句的作用域是指源代中可以有效使用這個名字的范
个声明语句将程序中的实体和一名字关联,比如一个函数或一个变量。声明语句的作用域是指源代中可以有效使用这个名字的范
不要作用域和生命期混爲一談。聲明語句的作用域對應的是一源代的文本域;它是一個編譯時的屬性。一個變量的生命期是指程序運行時變量存在的有效時間段,在此時間區域內它可以被程序的其他部分引用;是一個運行時的概念。
不要作用域和生命期混为一谈。声明语句的作用域对应的是一源代的文本域;它是一个编译时的属性。一个变量的生命期是指程序运行时变量存在的有效时间段,在此时间区域内它可以被程序的其他部分引用;是一个运行时的概念。
語法塊是由花括弧所包含的一繫列語句,就像函數體或循環體花括弧對應的語法塊那樣。語法塊內部聲明的名字是法被外部語法塊訪問的。語法決定了內部聲明的名字的作用域范。我可以這樣理解,語法塊可以包含其他類似組批量明等有用花括弧包含的代,我們稱之爲語法塊。有一個語法塊爲整個源代碼,稱爲全局語法塊;然是每包的包語法決;每for、if和switch句的語法決;每switch或select的分支也有立的語法決;當然也包括顯式書寫的語法塊(花括弧包含的句)。
语法块是由花括弧所包含的一系列语句,就像函数体或循环体花括弧对应的语法块那样。语法块内部声明的名字是法被外部语法块访问的。语法决定了内部声明的名字的作用域范。我可以这样理解,语法块可以包含其他类似组批量明等有用花括弧包含的代,我们称之为语法块。有一个语法块为整个源代码,称为全局语法块;然是每包的包语法决;每for、if和switch句的语法决;每switch或select的分支也有立的语法决;当然也包括显式书写的语法块(花括弧包含的句)。
聲明語句對應的詞法域定了作用域范的大小。對於內置的型、函和常量比如int、len和true等是在全局作用域的因此可以在整程序中直接使用。任何在在函外部(也就是包級語法域)明的名字可以在同一包的任何源文件中訪問的。對於導入的包例如tempconv入的fmt包則是對應源文件的作用域,因此能在前的文件中訪問導入的fmt包前包的其它源文件無法訪問在當前源文件入的包。還有許多聲明語比如tempconv.CToF函中的量c是局部作用域的,它能在函數內部(甚至能是局部的某些部分)訪問
声明语句对应的词法域定了作用域范的大小。对于内置的型、函和常量比如int、len和true等是在全局作用域的因此可以在整程序中直接使用。任何在在函外部(也就是包级语法域)明的名字可以在同一包的任何源文件中访问的。对于导入的包例如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式的初始化法域,一在for循環體詞法域;隻有兩個塊是顯式創建的:
下面的例子同有三不同的x量,每个声明在不同的法域,一在函数体词法域,一在for式的初始化法域,一在for循环体词法域;只有两个块是显式创建的:
```Go
func main() {
@@ -58,7 +58,7 @@ func main() {
}
```
和for循環類if和switch句也會在條件部分創建隱式詞法域,有它們對應的執行體詞法域。下面的if-else測試鏈演示了x和y的有效作用域范
和for循环类if和switch句也会在条件部分创建隐式词法域,有它们对应的执行体词法域。下面的if-else测试链演示了x和y的有效作用域范
```Go
if x := f(); x == 0 {
@@ -71,11 +71,11 @@ 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句的每分支也有似的法域规则:条件部分为一个隐式词法域,然后每个是每分支的法域。
在包别,明的順序併不會影響作用域范,因此一個先聲明的可以引用它自身或者是引用面的一個聲明,可以讓我們定義一些相互嵌套或遞歸的類型或函。但是如果一個變量或常量遞歸引用了自身,則會産生編譯錯誤
在包别,明的顺序并不会影响作用域范,因此一个先声明的可以引用它自身或者是引用面的一个声明,可以让我们定义一些相互嵌套或递归的类型或函。但是如果一个变量或常量递归引用了自身,则会产生编译错误
這個程序中:
这个程序中:
```Go
if f, err := os.Open(fname); err != nil { // compile error: unused: f
@@ -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)
@@ -98,7 +98,7 @@ f.ReadByte()
f.Close()
```
你可能會考慮通過將ReadByte和Close移到if的else塊來解決這個問題
你可能会考虑通过将ReadByte和Close移到if的else块来解决这个问题
```Go
if f, err := os.Open(fname); err != nil {
@@ -110,9 +110,9 @@ if f, err := os.Open(fname); err != nil {
}
```
不是Go言推的做法Go言的習慣是在if中處理錯誤然後直接返迴,這樣可以保正常行的句不需要代碼縮進
不是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
@@ -141,9 +141,9 @@ func init() {
}
```
全局的cwd量依然是有被正初始化的,而且看似正常的日誌輸出更是讓這個BUG更加晦。
全局的cwd量依然是有被正初始化的,而且看似正常的日志输出更是让这个BUG更加晦。
多方式可以避免出現類似潛在的問題。最直接的方法是通過單獨聲明err量,避免使用`:=`簡短聲明方式:
多方式可以避免出现类似潜在的问题。最直接的方法是通过单独声明err量,避免使用`:=`简短声明方式:
```Go
var cwd string
@@ -157,5 +157,5 @@ func init() {
}
```
們已經看到包、文件、明和句如何來表達一個程序結構。在下面的兩個章節,我們將探討數據的結構
们已经看到包、文件、明和句如何来表达一个程序结构。在下面的两个章节,我们将探讨数据的结构