18. 理解Go 语言中的 语句块与做用域

Hi,你们好,我是明哥。python

在本身学习 Golang 的这段时间里,我写了详细的学习笔记放在个人我的微信公众号 《Go编程时光》,对于 Go 语言,我也算是个初学者,所以写的东西应该会比较适合刚接触的同窗,若是你也是刚学习 Go 语言,不防关注一下,一块儿学习,一块儿成长。git

个人在线博客:golang.iswbm.com 个人 Github:github.com/iswbm/GolangCodingTimegithub


因为 Go 使用的是词法做用域,而词法做用域依赖于语句块。因此在讲做用域时,须要先了解一下 Go 中的语句块是怎么一回事?golang

1. 显示语句块与隐式语句块

通俗地说,语句块是由花括弧({})所包含的一系列语句。shell

语句块内部声明的名字是没法被外部块访问的。这个块决定了内部声明的名字的做用域范围,也就是做用域。编程

用花括弧包含的语句块,属于显示语句块。数组

在 Go 中还有不少的隐式语句块:bash

  • 主语句块:包括全部源码,对应内置做用域
  • 包语句块:包括该包中全部的源码(一个包可能会包括一个目录下的多个文件),对应包级做用域
  • 文件语句块:包括该文件中的全部源码,对应文件级做用域
  • for 、if、switch等语句自己也在它自身的隐式语句块中,对应局部做用域

前面三点好理解,第四点举几个例子微信

for 循环完后,不能再使用变量 i 编程语言

for i := 0; i < 5; i++ {
    fmt.Println(i)
}复制代码

if 语句判断完后,一样不能再使用变量 i

if i := 0; i >= 0 {
    fmt.Println(i)
}复制代码

switch 语句完了后,也是否是再使用变量 i

switch i := 2; i * 4 {
case 8:
    fmt.Println(i)
default:
    fmt.Println(“default”)
}复制代码

且每一个 switch 语句的子句都是一个隐式的语句块

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 的代码演示一下,你就知道了

#!/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 变量的。

因此此时的输出结果是

func02 sees value as 1
func02 sees value as 复制代码

但在 Go 中并不存在这种动态做用域,好比这段代码,在func01函数中,要想取得 name 这个变量,只能从func01的做用域或者更高层做用域里查找(文件级做用域,包级做用域和内置做用域),而不能从调用它的另外一个局部做用域中(由于他们在层级上属于同一级)查找。

import "fmt"

func func01() {
    fmt.Println("在 func01 函数中,name:", name)
}

func main()  {
    var name string = "Python编程时光"
    fmt.Println("在 main 函数中,name:", name)

    func01()
}复制代码

所以你在执行这段代码时,会报错,提示在func01中的name还未定义。

参考文章:studygolang.com/articles/12…

系列导读

01. 开发环境的搭建(Goland & VS Code)

02. 学习五种变量建立的方法

03. 详解数据类型:**整形与浮点型**

04. 详解数据类型:byte、rune与string

05. 详解数据类型:数组与切片

06. 详解数据类型:字典与布尔类型

07. 详解数据类型:指针

08. 面向对象编程:结构体与继承

09. 一篇文章理解 Go 里的函数

10. Go语言流程控制:if-else 条件语句

11. Go语言流程控制:switch-case 选择语句

12. Go语言流程控制:for 循环语句

13. Go语言流程控制:goto 无条件跳转

14. Go语言流程控制:defer 延迟调用

15. 面向对象编程:接口与多态

16. 关键字:make 和 new 的区别?

17. 一篇文章理解 Go 里的语句块与做用域

18. 学习 Go 协程:goroutine

19. 学习 Go 协程:详解信道/通道

20. 几个信道死锁经典错误案例详解

21. 学习 Go 协程:WaitGroup

22. 学习 Go 协程:互斥锁和读写锁

23. Go 里的异常处理:panic 和 recover

24. 超详细解读 Go Modules 前世此生及入门使用

25. Go 语言中关于包导入必学的 8 个知识点

26. 如何开源本身写的模块给别人用?

27. 说说 Go 语言中的类型断言?

28. 这五点带你理解Go语言的select用法


相关文章
相关标签/搜索