Golang 作用域

1.Go的词法作用域使用块(block),我们需要先理解块的概念:

块指的是一系列的语句(语句为空的也是可以的)。块可以内嵌,并且被大括号包含:

package main

import (
    "fmt"
)
func main() {
    { // 外部块开始
        a := 1
        fmt.Println(a)
        { //内部块开始
            b := 2
            fmt.Println(b)
        } // 内部块结束
    } // 外部块结束
}

除了显式的块之外,还有隐式的块:

  1. universe块包含所有的Go源文本
  2. package块包含包中的所源文本(包可以在一个文件夹中,分布在多个文件中存放)
  3. 每个文件都有一个file块,它包含该文件中所有的Go源文本
  4. if、for、switch可以看做是在他们自己的隐式的块中
  5. “switch”或“select”语句中的每个子句都充当隐式块。
    for i := 1 ; i < 10 ; i ++ {
        fmt.Println("current num is ",i)
    }

变量i在初始化(init)语句中声明,它是在条件(condation)语句、函数体、post语句中可见的,如果在for语句之后使用变量i的话,你会的到编译异常。

if i := 0; i >= 0 {
    fmt.Println(i)
}

我们声明了变量i,他可以被条件表达式所使用,当条件表达式为true,那么就执行嵌套的块,否则就执行else子句

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

switch和if语句一样
根据语法,switch的每一个字句也是一个块,根据定义,我们需要为其添加两边的大括号,但是这样太不美观了。
2.作用域
我们通过声明将标识符绑定到变量、常量、包、类型、函数以及标签。
程序中的标识符必须声明,并且在相同的块中不可以有相同的声明,也没有标识符可以在package和file块中同时都声明

空白标识符可以像声明的任何其他标识符一样使用,但它不引入绑定,因此没有声明。在package块中,标识符init可能只用于初始化函数声明,和空白标识符一样,它不引入新的绑定。

声明的词法块决定了它的作用域,这个作用域可大可小。
在universe block中预声明了一些内建的类型,函数,还有常量,下面是总的:
Types:
bool byte complex64 complex128 error float32 float64
int int8 int16 int32 int64 rune string
uint uint8 uint16 uint32 uint64 uintptr

Constants:
true false iota

Zero value:
nil

Functions:
append cap close complex copy delete imag len
make new panic print println real recover
在任何函数之外的声明,也成为package level(包级),他可以被同包的任何文件所访问
而在文件中导入的包,他只能被当前文件访问,而不能被其他文件所访问(其他文件也不导入的前提下),这也被成为file level(文件级)

当编译器遇见对一个名字的引用时候,他会查找你对其的声明,从词法块开始一直到universe块为止,如果编译器没有发现声明,他会报出“undeclared name”错误。如果该名称有两个声明,分别处于不同的块中,那么会有限取内部块的声明,这也就是说内部的声明shadow或者hide了外部的声明,使其不可见。

注意:
词法作用域(lexical scope) = 静态作用域(static scope)
动态作用域(dynamically scope)

对于控制流标签,如break,continye以及goto语句,他们作用域是整个封闭的函数
下面做两种作用域的对比

//在动态作用域的语言中
func f(){
    x := 1
    g()
}
func g(){
    fmt.Println(x) //是可以的。
}

//静态作用域
func f(){
    x := 1
    g()
}
func g(){
    fmt.Println(x) //无法访问
}
package main
import “fmt”
func main() {
    {
        v := 1
        {
            fmt.Println(v)
        }
        fmt.Println(v)
    }
    // “undefined: v” compilation error
    // fmt.Println(v)
}

对于有编程经验的工程师而言,完全可以理解
最后调用fmt.Println被注释掉,因为它会导致编译错误。很快就会澄清为什么会这样。短变量v在包含其声明的右花括号后即超出了其作用域
值得一提的是,为变量分配新值并不会影响作用域(即可见性):

v := 1  //声明
{
    v = 2  // 赋值
    fmt.Println(v)
}
fmt.Println(v) //依然可以访问

但是下面的情景即不一样了:

v := 1 // 短变量声明
{
    v := 2  // 短变量声明
    fmt.Println(v)
}
fmt.Println(v)

作用域与标识符的声明息息相关

猜你喜欢

转载自blog.csdn.net/qq_31179577/article/details/81458333