作用域
函数会开辟一个局部作用域,其中定义的标识符仅能在函数之中使用,也称为标识符在函数中的可见范围。 这种对标识符约束的可见范围,称为作用域。
对外不可见、向内穿透、就近原则。
1、语句块作用域
if、for、switch等语句中定义的变量,可以认为就是该语句块的变量,作用域仅在该语句块中。if、swith、select语句中的每个子句都被视为一个隐式的代码块。
s0 := []int{1, 2, 3, 4, 5} if s0[1] >10 { var a int =10 fmt.Println(10) }else if s0[1] < 10 { fmt.Println(a) // 此处对变量a不可见 }
2、显式的块作用域
在任何一个大括号中定义的标识符,其作用域只能在这对大括号中。
{ var a string = "你好gopher" const b = 123 c := 'a' fmt.Println(a,b,c) } fmt.Println(a,b,c) // 此处对abc变量不可见
3、universe块
宇宙块,意思就是全局块,不过是语言内建的。预定义的标识符就在这个全局环境中,因此bool、int、 nil、true、false、iota、append等标识符全局可见,随处可用。
4、包块
每一个package包含该包所有源文件,形成的作用域。有时在包中顶层代码定义标识符,也称为全局标 识符。 所有包内定义全局标识符,包内可见。包的顶层代码中标识符首字母大写则导出,从而包外可见,使用 时也要加上包名。例如 fmt.Prinf() 。
// 下面是源码 func Println(a ...any) (n int, err error) { return Fprintln(os.Stdout, a...) }
5、函数块
函数声明的时候使用了花括号,所以整个函数体就是一个显式代码块。这个函数就是一个块作用域。
const a = 123 var b string = "你好gopher" var c = 768 func show() int { return c } func main() { fmt.Printf("%T %[1]v\n", a) // 这里的a是全局常量a,常量不可取地址 a := "gopher" fmt.Printf("%T %[1]v %p\n", a, &a) // 这里的a是上面刚定义的局部变量a fmt.Printf("%T %[1]v %p\n", c, &c) c = 1024 fmt.Printf("%T %[1]v %p\n", c, &c) var c = 'c' fmt.Printf("%T %[1]v %p\n", c, &c) fmt.Println(show()) // 这里show函数return的c是被修改成1024的全局变量的c }
标识符作用域
标识符对外不可见,在标识符定义所在作用域外是看不到标识符的
使用标识符,自己这一层定义的标识符优先,如果没有,就向外层找同名标识符——自己优先,由近及远
标识符对内可见,在内部的局部作用域中,可以使用外部定义的标识符——向内穿透
包级标识符,在所在包内,都可见;跨包访问,包级标识符必须大写开头,才能导出到包外,可以在包外使用 xx包名.VarName 方 式访问。例如 fmt.Print()
匿名函数
顾名思义,就是没有名字的函数。在函数定义中,把名字去掉就可以了。
func main() { func(x, y int) int { res := x + y fmt.Println(res) return res }(10, 11) // 定义并调用 // 使用变量指向一个匿名函数,通过变量来调用以实现n次调用 var add = func(x, y int) int { return x + y } fmt.Println(add(1, 2)) }
匿名函数主要作用是用作高阶函数中,它是传入的逻辑。若一个函数允许传入的参数是函数类型,就是 把操作逻辑外置。
高阶函数:形参定义为函数类型或返回值为函数类型,称为高阶函数
// cal并没有实现x,y的操作,而是交给了fu,而fu究竟做什么操 // 作由未来的使用者决定 func cal(x, y int, fu func(int, int) int) int { return fu(x, y) } func main() { add_re := cal(3, 5, func(a, b int) int { return a + b }) mul_re := cal(10, 22, func(c, d int) int { return c * d }) fmt.Println(add_re, mul_re) }
函数嵌套
outer中定义了另外一个函数inner,并且调用了inner。outer是包级变量,main可见,可以调 用。而inner是outer中的局部变量,outer中可见。