深入学习Go语言
上QQ阅读APP看书,第一时间看更新

第3章 作用域

3.1 关于作用域

3.1.1 局部变量与全局变量

(1)局部变量

Go语言中,在函数体内或代码块内声明的变量称为局部变量,它们的作用域只在代码块内,参数和返回值变量也是局部变量。

(2)全局变量

Go语言中,在函数体外声明的变量称为全局变量,它们的作用域都是全局的(在本包范围内)。全局变量可以在整个包甚至外部包(被导出后)使用。

全局变量可以在任何函数中使用。

(3)简式变量

Go语言中使用 :=声明的变量,一般也是局部变量,如果新局部变量Ga与同名已定义变量(即全局变量Ga)不在一个作用域中,Go语言会在此作用域新定义这个局部变量Ga,遮盖住全局变量Ga。Go语言初学者刚开始很容易在此犯错,解决方法是局部变量尽量不同名。

3.1.2 显式与隐式代码块

根据Go语言的规范,Go语言中的标识符作用域是基于代码块(code block)的。代码块是包裹在一对花括号{}内部的声明和语句,并且是可嵌套的,在代码中是直观的、显式的(explicit),比如:函数的函数体、for循环的循环体等。还有隐式的(implicit)代码块。

使用最多的if语句类型只有if而没有else分支,如:

在这种类型的if语句中,有两个代码块:一个隐式的代码块和一个显式的代码块。把上面的代码做一个等价变化,并加上代码块起始和结束点的标注,结果如下:

下面的代码综合了几种作用域的情况,很容易混淆。请读者仔细琢磨。

程序输出:

Ga作为全局变量是int类型,值为99;而在main()中,Ga通过简式声明 :=操作,是string类型,值为string。在main()中,v很典型地体现了在花括号中的作用域问题,每一层花括号,都是对上一层的屏蔽。闭包函数中,GetGa()返回的匿名函数赋值给b,每次执行b(),Ga的值都存在内存中,下次执行b()的时候,取b()上次执行后Ga的值,而不是全局变量Ga的值,这就是闭包函数可以使用包含它的函数内的变量的原因,因为闭包作为代码块一直存在,所以每次执行都是在上次基础上运行。

简单总结如下:

(1)有花括号一般都存在作用域的划分。

(2):=简式声明会屏蔽所有上层代码块中的变量(常量),建议使用开发约定来减少变量被屏蔽的可能,如对常量使用全部大写,对局部变量涵盖命名等。

(3)在if等语句中存在隐式代码块,需要注意。

(4)闭包函数可以理解为一个代码块,并且可以使用包含它的函数内的变量。

简式声明变量只能在函数内部出现,它会覆盖函数外的同名全局变量。简式声明 :=左侧只有一个变量的情况下不能重复声明一个变量,有多个变量时是允许的,但这些变量中至少要有一个新的变量。重复声明的变量需要在同一层代码块内,否则将得到一个隐藏变量。

如果在代码块中犯了这个错误,不会出现编译错误,但应用运行结果可能不是你所期望的。所以应尽量避免和全局变量同名。

作用域问题思考:下面这段代码的运行结果是什么,你能写出来吗?