ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、视频、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
## 作用域 一个声明语句将程序中的实体和一个名字关联,比如一个函数或一个变量。声明语句的作用域是指源代码中可以有效使用这个名字的范围。 **不要将作用域和生命周期混为一谈**。声明语句的作用域对应的是一个源代码的文本区域;它是一个编译时的属性。一个变量的生命周期是指程序运行时变量存在的有效时间段,在此时间区域内它可以被程序的其他部分引用;是一个运行时的概念。 声明语句对应的词法域决定了作用域范围的大小。对于内置的类型、函数和常量,比如int、len和true等是在全局作用域的,因此可以在整个程序中直接使用。 任何在在函数外部(也就是包级语法域)声明的名字可以在同一个包的任何源文件中访问的。 一个程序可能包含多个同名的声明,只要它们在不同的词法域就没有关系。例如,你可以声明一个局部变量,和包级的变量同名。 当编译器遇到一个名字引用时,如果它看起来像一个声明,它首先从最内层的词法域向全局的作用域查找。如果查找失败,则报告“未声明的名字”这样的错误。如果该名字在内部和外部的块分别声明过,则内部块的声明首先被找到。在这种情况下,内部声明屏蔽了外部同名的声明,让外部的声明的名字无法被访问: ## 隐式词法块 for0循环,if和switch语句也会在条件部分创建隐式词法域,还有它们对应的执行体词法域。在if声明的变量在else中也可以使用。 ~~~ func main() { if s := show(); s == "world" { fmt.Println("no") } else { fmt.Println(s) } } func show() string { return "hello" } ~~~ 在包级别,声明的顺序并不会影响作用域范围,因此一个先声明的可以引用它自身或者是引用后面的一个声明,这可以让我们定义一些相互嵌套或递归的类型或函数。但是如果一个变量或常量递归引用了自身,则会产生编译错误。 ## 短变量声明容易产生bug的地方 要特别注意短变量声明语句的作用域范围,考虑下面的程序,它的目的是获取当前的工作目录然后保存到一个包级的变量中。这可以本来通过直接调用os.Getwd完成,但是将这个从主逻辑中分离出来可能会更好,特别是在需要处理错误的时候。函数log.Fatalf用于打印日志信息,然后调用os.Exit(1)终止程序。 ~~~ var cwd string func init() { cwd, err := os.Getwd() // compile error: unused: cwd 这里的cwd和包级的cwd没有关系,这是一个新变量 if err != nil { log.Fatalf("os.Getwd failed: %v", err) } log.Printf("Working directory = %s", cwd) //这里的打印结果会让这个BUG更加隐晦 } ~~~