从零学习 Go 语言(18):Go 语言中的 语句块与作用域

hello_wbm · · 601 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

![](http://image.iswbm.com/20200607145423.png) 在线博客:http://golang.iswbm.com/ Github:https://github.com/iswbm/GolangCodingTime --- 由于 Go 使用的是词法作用域,而词法作用域依赖于语句块。所以在讲作用域时,需要先了解一下 Go 中的语句块是怎么一回事? ## 1. 显示语句块与隐式语句块 通俗地说,语句块是由花括弧(`{}`)所包含的一系列语句。 语句块内部声明的名字是无法被外部块访问的。这个块决定了内部声明的名字的作用域范围,也就是作用域。 用花括弧包含的语句块,属于显示语句块。 在 Go 中还有很多的隐式语句块: - 主语句块:包括所有源码,对应内置作用域 - 包语句块:包括该包中所有的源码(一个包可能会包括一个目录下的多个文件),对应包级作用域 - 文件语句块:包括该文件中的所有源码,对应文件级作用域 - for 、if、switch等语句本身也在它自身的隐式语句块中,对应局部作用域 前面三点好理解,第四点举几个例子 for 循环完后,不能再使用变量 i ```go for i := 0; i < 5; i++ { fmt.Println(i) } ``` if 语句判断完后,同样不能再使用变量 i ```go if i := 0; i >= 0 { fmt.Println(i) } ``` switch 语句完了后,也是不是再使用变量 i ```go switch i := 2; i * 4 { case 8: fmt.Println(i) default: fmt.Println(“default”) } ``` 且每个 switch 语句的子句都是一个隐式的语句块 ```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 的代码演示一下,你就知道了 ```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 变量的。 所以此时的输出结果是 ```shell func02 sees value as 1 func02 sees value as ``` 但在 Go 中并不存在这种动态作用域,比如这段代码,在func01函数中,要想取得 name 这个变量,只能从func01的作用域或者更高层作用域里查找(文件级作用域,包级作用域和内置作用域),而不能从调用它的另一个局部作用域中(因为他们在层级上属于同一级)查找。 ```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 ## 系列导读 --- [从零学习 Go 语言(01):一文搞定开发环境的搭建](https://studygolang.com/articles/27365) [从零学习 Go 语言(02):学习五种变量创建的方法](https://studygolang.com/articles/27432) [从零学习 Go 语言(03):数据类型之整型与浮点型](https://studygolang.com/articles/27440) [从零学习 Go 语言(04):byte、rune与字符串](https://studygolang.com/articles/27463) [从零学习 Go 语言(05):数据类型之数组与切片](https://studygolang.com/articles/27508) [从零学习 Go 语言(06):数据类型之字典与布尔类型](https://studygolang.com/articles/27563) [从零学习 Go 语言(07):数据类型之指针](https://studygolang.com/articles/27585) [从零学习 Go 语言(08):流程控制之if-else](https://studygolang.com/articles/27613) [从零学习 Go 语言(09):流程控制之switch-case](https://studygolang.com/articles/27660) [从零学习 Go 语言(10):流程控制之for 循环](https://studygolang.com/articles/28120) [从零学习 Go 语言(11):goto 无条件跳转](https://studygolang.com/articles/28472) [从零学习 Go 语言(12):流程控制之defer 延迟语句](https://studygolang.com/articles/28515) [从零学习 Go 语言(13):异常机制 panic 和 recover](https://studygolang.com/articles/28519) [从零学习 Go 语言(14):Go 语言中的类型断言是什么?](https://studygolang.com/articles/29305) [从零学习 Go 语言(15):学习 Go 语言的结构体与继承](https://studygolang.com/articles/29306) [从零学习 Go 语言(17):Go 语言中的 make 和 new 有什么区别?](https://studygolang.com/articles/29315) --- ![](http://image.python-online.cn/20200321153457.png)

有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

601 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传