引言:该系列笔记,是本人自学Go过程中整理的笔记,供大家参考。
词法域和词法块的定义
作用域和生命周期是两个概念
- 作用域:声明语句的作用域对应的是一个源代码的文本区域;它是一个编译时的属性
- 生命周期:一个变量的生命周期是指程序运行时变量存在的有效时间段,在此时间段之内可以被程序的其他部分引用;是一个运行时的概念
句法块是由花括弧所包含的一系列语句,比如由函数体或循环体包裹的内容一样。句法块内部声明的名字无法被外部访问。
词法块是指哪些没有在代码中显式地使用花括号包裹的声明;整个源代码文件就是一个词法块,为全局词法块;对于每个包、每个for、if和Switch语句也都有对应的词法块;哪些显式书写的词法块也算(就是被花括号包裹的),词法块中声明的变量不会被覆盖。
变量的优先级
优先级第一: 内置的类型、函数和常量优先级最高
优先级第二: 对于导入包中的包级名字(首字母大写),可以在当前导入包的源文件中随意使用。包中任意源文件声明的包级名字可以在包内随意访问(不要求首字母大写)。
优先级第三: 源文件中在词法块内部声明的名字,只能在内部引用,外部无法直接引用。
变量的访问顺序
当编译器访问一个变量的时候,会对其定义进行查找,查找过程是从最内部的词法块开始向全局作用域查找。如果没有找到就会报编译错误。如果内部已经声明了一个变量,外部词法块重新声明了变量。那么内部变量会覆盖外部变量。
package main
import "fmt"
var testStr string = "wait,I try to be init again"
func main() {
testStr := "I try to be init"
fmt.Println(testStr) // 最后输出的结果是 I try to be init
}
词法域的作用范围
先看这段代码:
package main
import(
"fmt"
)
func main() {
x := "hello"
for _, x := range x {
x := x + 'A' - 'a'
fmt.Printf("%c", x) // 最后输出的结果是HELLO
}
}
这里一共有三个不同的x变量,其中有两个是显式声明的,一个是隐式声明的。for循环体花括号内和main()函数花括号内显式声明了x变量,for循环体隐式的初始化了一个x变量。main函数嵌套for循环体,所以main函数初始化的x变量可以在for循环体中也可以访问。
再看这段代码:
package main
import (
"log"
"os"
)
var cwd string
func main() {
cwd, err := os.Getwd()
if err != nil {
log.Fatalf("os.Getwd fail, %v", err) // compile error
}
}
这里会报编译错误,为什么? 因为遵循由内向外的原则,首先找到main函数内部通过隐式初始化了cwd,这时外部声明的cwd已经没有,被覆盖了。go强调初始化的变量是一定要用到的,没有用到就会编译不通过。
其实这个问题,有可能会被忽略;比如在main函数内部用下cwd变量就能避免编译报错的问题:log.Println("working directory is s%", cwd)
;但是外部声明的cwd就一直没有被用到,是个bug。
所以最好的解决方式,就是直接声明err变量,不要隐式声明最好。var err error
就完了。