1.15 语法规则:理解语句块与作用域 ================================= .. image:: http://image.iswbm.com/20200607145423.png 由于 Go 使用的是词法作用域,而词法作用域依赖于语句块。所以在讲作用域时,需要先了解一下 Go 中的语句块是怎么一回事? 1. 显示语句块与隐式语句块 ------------------------- 通俗地说,语句块是由花括弧(\ ``{}``\ )所包含的一系列语句。 语句块内部声明的名字是无法被外部块访问的。这个块决定了内部声明的名字的作用域范围,也就是作用域。 用花括弧包含的语句块,属于显示语句块。 在 Go 中还有很多的隐式语句块: - 主语句块:包括所有源码,对应内置作用域 - 包语句块:包括该包中所有的源码(一个包可能会包括一个目录下的多个文件),对应包级作用域 - 文件语句块:包括该文件中的所有源码,对应文件级作用域 - for 、if、switch等语句本身也在它自身的隐式语句块中,对应局部作用域 前面三点好理解,第四点举几个例子 for 循环完后,不能再使用变量 i .. code:: go for i := 0; i < 5; i++ { fmt.Println(i) } if 语句判断完后,同样不能再使用变量 i .. code:: go if i := 0; i >= 0 { fmt.Println(i) } switch 语句完了后,也是不是再使用变量 i .. code:: go switch i := 2; i * 4 { case 8: fmt.Println(i) default: fmt.Println(“default”) } 且每个 switch 语句的子句都是一个隐式的语句块 .. code:: go switch i := 2; i * 4 { case 8: j := 0 fmt.Println(i, j) default: // "j" is undefined here fmt.Println(“default”) } // "j" is undefined here 2. 四种作用域的理解 ------------------- 变量的声明,除了声明其类型,其声明的位置也有讲究,不同的位置决定了其拥有不同的作用范围,说白了就是我这个变量,在哪里可用,在哪里不可用。 根据声明位置的不同,作用域可以分为以下四个类型: - 内置作用域:不需要自己声明,所有的关键字和内置类型、函数都拥有全局作用域 - 包级作用域:必須函数外声明,在该包内的所有文件都可以访问 - 文件级作用域:不需要声明,导入即可。一个文件中通过import导入的包名,只在该文件内可用 - 局部作用域:在自己的语句块内声明,包括函数,for、if 等语句块,或自定义的 {} 语句块形成的作用域,只在自己的局部作用域内可用 以上的四种作用域,从上往下,范围从大到小,为了表述方便,我这里自己将范围大的作用域称为高层作用域,而范围小的称为低层作用域。 对于作用域,有以下几点总结: - 低层作用域,可以访问高层作用域 - 同一层级的作用域,是相互隔离的 - 低层作用域里声明的变量,会覆盖高层作用域里声明的变量 在这里要注意一下,不要将作用域和生命周期混为一谈。声明语句的作用域对应的是一个源代码的文本区域;它是一个编译时的属性。 而一个变量的生命周期是指程序运行时变量存在的有效时间段,在此时间区域内它可以被程序的其他部分引用;是一个运行时的概念。 3. 静态作用域与动态作用域 ------------------------- 根据局部作用域内变量的可见性,是否是静态不变,可以将编程语言分为如下两种: - 静态作用域,如 Go 语言 - 动态作用域,如 Shell 语言 具体什么是动态作用域,这里用 Shell 的代码演示一下,你就知道了 .. code:: python #!/bin/bash func01() { local value=1 func02 } func02() { echo "func02 sees value as ${value}" } # 执行函数 func01 func02 从代码中,可以看到在 func01 函数中定义了个局部变量 value,按理说,这个 value 变量只在该函数内可用,但由于在 shell 中的作用域是动态的,所以在 func01中也可以调用 func02 时,func02 可以访问到 value 变量,此时的 func02 作用域可以当成是 局部作用域中(func01)的局部作用域。 但若脱离了 func01的执行环境,将其放在全局环境下或者其他函数中, func02 是访问不了 value 变量的。 所以此时的输出结果是 .. code:: shell func02 sees value as 1 func02 sees value as 但在 Go 中并不存在这种动态作用域,比如这段代码,在func01函数中,要想取得 name 这个变量,只能从func01的作用域或者更高层作用域里查找(文件级作用域,包级作用域和内置作用域),而不能从调用它的另一个局部作用域中(因为他们在层级上属于同一级)查找。 .. code:: go import "fmt" func func01() { fmt.Println("在 func01 函数中,name:", name) } func main() { var name string = "Python编程时光" fmt.Println("在 main 函数中,name:", name) func01() } 因此你在执行这段代码时,会报错,提示在func01中的name还未定义。 参考文章:https://studygolang.com/articles/12632