1 命名空间和作用域
1.1 命名空间
命名空间(Namespace)在编程语言中常用来表示标识符(identifier)的可见范围。编程语言借助命名空间来解决标识符不能同名的问题,命名空间实际上相当于给标识符添加了标识前缀,使标识符变得全局唯一。另外,命名空间是程序组织更加模块化,降低了程序内部的耦合性。
一个标识符可以在多个命名空间中定义,它在不同命名空间中的含义是不互相干的。新的命名空间中可定义任意的标识符,它们不会与位于其他命名空间上的同名标识符发生冲突,当然自定义标识符尽量不要使用编程语言自身的关键字,因为这些标识符具有全局作用域。
Go语言继承了命名空间的概念,采用包来组织代码,包名构成Go命名空间的一部分,不同的包就是一个独立的命名空间。
Go语言除了包级显式的命名空间,还有隐式的命名空间。函数、方法,以及 if、for、switch 等和 “{ }” 一起构成了一个个代码块,代码块可以嵌套代码块,每一个代码块都构成一个隐式的命名空间。
不同命名空间可以声明相同的标识符,所以不同的隐式的命名空间同样允许声明相同的标识符(包括变量),这里就有变量覆盖的问题。在介绍变量覆盖之前,先来介绍作用域。
1.2 作用域
在高级编程语言中,作用域(scope)是指名字(name)与实体(可以理解为特定内存地址)的绑定(binding)保持有效的那部分程序逻辑区间。Go语言是静态作用域的编程语言。所谓静态作用域就是变量的作用域不依赖程序执行时的因素,变量作用域在编译器就能确定。
Go语言有三种类型的作用域:
- 全局作用域
在任何地方都可以访问的标识符,称其具有全局作用域。在Go语言中,全局作用域有两类:
(1)Go语言内置的预声明标识符(包括预声明的类型名、关键字、内置函数等),它们具有全局作用域,在任意命名空间内都可见。
(2)Go语言包内以大写字母开头的标识符(包括变量、常量、函数和方法名、自定义类型、结构体字段等),它们具有全局作用域,在任意命名空间内都可见。
- 包内作用域
在Go语言包内以小写字母开头的标识符(包括变量、常量、函数和方法名、自定义类型、结构体字段等),它们在本包可见,在其他包都是不可见的,这些标识符具有包内作用域。
- 隐式作用域
每个代码块内定义的变量称为“局部变量”,这些局部变量只在当前代码块内可见,其作用域属于当前代码块的隐式作用域。
1.3 变量覆盖
Go语言编译器解析变量名到引用实体采用的是从里到外的搜索模式,里层的局部变量能够覆盖掉外层变量,使得同名的外层变量不可见,这种现象称为变量覆盖。
由于命名空间允许同名变量的存在,大量的同名变量可能会给程序的可读性带来影响,Go语言通过包、代码缩进和大括号“{ }” 组织嵌套的方式来改善程序的可读性,这也是众多编程语言的通用做法。一些临时变量可以同名,一些关键变量还是尽量起一个有意义的名称。
2 包的基本概念
2.1 基本概念
Go语言的源码复用是建立在包(package)基础之上。Go语言的入库 main() 函数所在的包(package)叫 main 包,main包想要引用别的代码,必须同样以包的方式进行引用。Go语言使用包来组织代码的,并实现命名空间的管理。任何源代码文件必须属于某个包。源码文件的第一行有效代码必须是 package pkgName 语句,通过该语句声明自己所在的包。
Go语言的包借助了目录树的组织形式,一般包的名称就是其源文件所在目录的名称,虽然Go没有强制包名必须和其所在的目录名同名,但还是建议包名和所在目录同名,这样结构更清晰。包可以定义在很深的目录中,包的定义是不包括路径的,但是包的引用一般是全路径引用。比如在 $GOPATH/src/a/b 下定义一个包 c,在包 c 的源码中只需要声明为 package c,而不是声明为 package a/b/c,但是在 import 包 c 时,需要带上路径 import a/b/c。包的引用有两种形式,在下面的内容中会详细介绍。
包的习惯用法&#x