3.3 作用域
作用域(Scope)对于Go语言来说是一个非常重要的概念。我们知道一段程序代码中所用到的标识符(变量名、常量名和函数名等)并不总是对外可见的,而限定这个标识符可用性的代码范围就是这个标识符的作用域。作用域为已声明标识符所表示的常量、类、变量、函数或包在源代码中的作用范围。
作用域机制可以大大降低标识符的命名冲突问题,防止全局污染,同时可以让临时使用的标识符(如变量)只在其作用域范围内可见、而在作用域外的区域无法访问,从而增强程序的可靠性。
举个例子,在学校读书的时候,我们可以用学校的IC卡到食堂购物,也就是说这个IC卡的作用域只能在学校内部范围使用,在学校外这个IC卡就失去了购物的作用,就相当于“不可见”了。
Go语言中的变量有全局变量和局部变量之分。一般来说,Go函数内定义的变量(参数和用返回值的变量)就是局部变量,其作用域只在函数体内;函数外定义的变量就是全局变量。在同一个作用域中,一个标识符不能被声明两次。但是,在函数内部的代码块中可以定义和函数外同名的标识符,按照就近原则,函数体内的标识符会暂时屏蔽外部的同名标识符。下面是演示变量作用域的示例程序3-4。
示例程序3-4 变量作用域:chapter03\code04\func.go
在示例程序3-4中,第04行和第05行分别定义了一个字符串类型的变量cname和整数类型的变量age。由于这两个变量是在函数外定义的,因此这两个变量是全局变量(对于同一个包而言)。第08行在函数printName中,定义了一个和第04行同名的变量cname,注意这是在函数体内声明的,是局部变量,因此只能在声明它的函数体内进行存取。当这个变量和外部变量同名时,函数体内引用的变量cname的值为内部定义局部变量的值,即"smith",而不是"jack"。
第10行对全局变量age进行自增。在Go语言中,++和--运算符只能当成一个语句来调用,而不可以将其直接赋值给变量或者当成一个参数传递给一个函数,因此第12行被注释的语句如果取消注释,编译器则会报错。这也是第10行要单独成一行的原因。
第15行在main入口函数中首先调用函数printName,虽然函数printName中定义了一个和cname同名的变量,但是不会影响全局变量cname的值,因此第16行输出的是全局变量在第04行的值。第17行输出全局变量age时,由于age在调用printName函数时被自增加了1,因此输出结果为31+1=32。
注意
即使是在不同的文件中,Go函数体外声明的全局变量在同名包内是可见的。这种全局变量如果变量名的首字母是小写的,那么在包外是不可见的;如果变量名的首字母是大写的,那么在包外是可见的。
下面介绍一下Go语言中常见的几种函数类型:匿名函数、变长函数、递归函数、回调函数和闭包。这几种函数在定义的时候都存在差异,而且使用场景一般也不同。