Go语言核心36讲
郝林
《Go并发编程实战》作者,前轻松筹大数据负责人
立即订阅
24128 人已学习
课程目录
已完结 54 讲
0/4登录后,你可以任选4讲全文学习。
开篇词+学习路线 (3讲)
开篇词 | 跟着学,你也能成为Go语言高手
免费
预习篇 | 写给0基础入门的Go语言学习者
50 | 学习专栏的正确姿势
模块一:Go语言基础知识 (6讲)
01 | 工作区和GOPATH
02 | 命令源码文件
03 | 库源码文件
04 | 程序实体的那些事儿(上)
05 | 程序实体的那些事儿(中)
06 | 程序实体的那些事儿 (下)
模块二:Go语言进阶技术 (16讲)
07 | 数组和切片
08 | container包中的那些容器
09 | 字典的操作和约束
10 | 通道的基本操作
11 | 通道的高级玩法
12 | 使用函数的正确姿势
13 | 结构体及其方法的使用法门
14 | 接口类型的合理运用
15 | 关于指针的有限操作
16 | go语句及其执行规则(上)
17 | go语句及其执行规则(下)
18 | if语句、for语句和switch语句
19 | 错误处理(上)
20 | 错误处理 (下)
21 | panic函数、recover函数以及defer语句 (上)
22 | panic函数、recover函数以及defer语句(下)
模块三:Go语言实战与应用 (27讲)
23 | 测试的基本规则和流程 (上)
24 | 测试的基本规则和流程(下)
25 | 更多的测试手法
26 | sync.Mutex与sync.RWMutex
27 | 条件变量sync.Cond (上)
28 | 条件变量sync.Cond (下)
29 | 原子操作(上)
30 | 原子操作(下)
31 | sync.WaitGroup和sync.Once
32 | context.Context类型
33 | 临时对象池sync.Pool
34 | 并发安全字典sync.Map (上)
35 | 并发安全字典sync.Map (下)
36 | unicode与字符编码
37 | strings包与字符串操作
38 | bytes包与字节串操作(上)
39 | bytes包与字节串操作(下)
40 | io包中的接口和工具 (上)
41 | io包中的接口和工具 (下)
42 | bufio包中的数据类型 (上)
43 | bufio包中的数据类型(下)
44 | 使用os包中的API (上)
45 | 使用os包中的API (下)
46 | 访问网络服务
47 | 基于HTTP协议的网络服务
48 | 程序性能分析基础(上)
49 | 程序性能分析基础(下)
尾声与思考题答案 (2讲)
尾声 | 愿你披荆斩棘,所向无敌
新年彩蛋 | 完整版思考题答案
Go语言核心36讲
登录|注册

05 | 程序实体的那些事儿(中)

郝林 2018-08-22
在前文中,我解释过代码块的含义。Go 语言的代码块是一层套一层的,就像大圆套小圆。
一个代码块可以有若干个子代码块;但对于每个代码块,最多只会有一个直接包含它的代码块(后者可以简称为前者的外层代码块)。
这种代码块的划分,也间接地决定了程序实体的作用域。我们今天就来看看它们之间的关系。
我先说说作用域是什么?大家都知道,一个程序实体被创造出来,是为了让别的代码引用的。那么,哪里的代码可以引用它呢,这就涉及了它的作用域。
我在前面说过,程序实体的访问权限有三种:包级私有的、模块级私有的和公开的。这其实就是 Go 语言在语言层面,依据代码块对程序实体作用域进行的定义。
包级私有和模块级私有访问权限对应的都是代码包代码块,公开的访问权限对应的是全域代码块。然而,这个颗粒度是比较粗的,我们往往需要利用代码块再细化程序实体的作用域。
比如,我在一个函数中声明了一个变量,那么在通常情况下,这个变量是无法被这个函数以外的代码引用的。这里的函数就是一个代码块,而变量的作用域被限制在了该代码块中。当然了,还有例外的情况,这部分内容,我留到讲函数的时候再说。
总之,请记住,一个程序实体的作用域总是会被限制在某个代码块中,而这个作用域最大的用处,就是对程序实体的访问权限的控制。对“高内聚,低耦合”这种程序设计思想的实践,恰恰可以从这里开始。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《Go语言核心36讲》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(45)

  • 衡子 置顶
    希望文字能再精简些,很绕!看起来比较费劲!当然内容还是不错的!
    2018-08-22
    2
    192
  • 咖啡色的羊驼 置顶
    会报redeclared。
    采用import . xxx如文章所说,基本上就会认为引入的代码包的代码,如同在本包中一样,那作用域其实是同一个,自然不允许重复声明。
    后文期待作者提到变量逃逸的问题,这个还蛮有趣的。

    2018-08-22
    1
    32
  • êwěn 置顶
    如果都是全局的变量,会报重复声明,如果只是在函数体重新声明,作用域不一样,应该不会报错吧

    作者回复: 对,作用域不一样,会出现屏蔽现象。

    2018-08-22
    25
  • atompi 置顶
    从此地铁上那半小时变得格外充实👍
    2018-08-22
    14
  • 衡子 置顶
    内容很不错👍 文字能再精简些就更好了!
    2018-08-22
    9
  • @hl 置顶
    什么是全域代码块

    作者回复: 一个Go程序可以访问到的所有代码共同组成的代码块,也可以理解为Go标准库和所有工作区中的代码共同组成的代码块。

    2018-08-22
    4
  • xiaolonghuster 置顶
    对于和重名变量,不同类型时,可在使用之前通过查看变量类型来确定使用的是哪个地方定义的变量,reflect.TypeOf(container)
    2018-08-22
    2
  • javaadu 置顶
    变量重复声明和变量重名不是一个事情,上篇讲的是前者,这一篇讲后者。变量重名按照作用域进行屏蔽,查找过程从小作用域查到大作用域

    作者回复: 很对。

    2018-08-22
    2
  • lik0914 置顶
    模块级私有,怎么全是模块级别呢
    2018-08-22
    1
  • 云学 置顶
    这种变量命名算是code smell么?

    作者回复: 可重名变量如果没用对地方就算

    2018-08-22
  • 小小笑儿 置顶
    报错,重定义
    2018-08-22
  • 卖轮子
    文字多 看着比较费劲。 而且为啥咋感觉每句话都很长而且绕口呢?

    作者回复: 这个专栏是以音频为主的,文字版是备份和巩固。有的句子里概念较多,可以多读两遍试试。

    2018-08-22
    25
  • 我来也
    当初在学校学c时,老师说重名变量时用了个很形象的比喻:强龙不压地头蛇。

    作者回复: 赞

    2018-08-22
    20
  • 我不会算法
    最后的思考题,应该分情况对待!
    1、如果子包中,声明的变量使用的是小写,如:var str = "abc" ,那么,在main包中同样声明 var str = "aaa" 是没有任何问题的,编译通过;
    2、如果子包中声明变量是大写,如:var Str = "abc",在main包中同样声明 var Str = "aaa",IDE工具提示'Str' redeclared in this package。但是在执行 `go run main.go` 或 `go run *` 时,代码是没有任何错误的;
    测试代码如下:

    ```
    package main // main包代码

    import "fmt"
    import . "geektime/go05/t1/p1"

    var Str = "aaa"

    func main() {
    fmt.Println(Str)

    P()
    }
    ```

    ```
    package p1 // 子包p1的代码

    import "fmt"

    var Str = "abc"

    func P() {
    fmt.Println(Str)
    }
    ```
    代码编译OK,执行结果为
    ```
    aaa
    abc
    ```
    2018-10-10
    5
  • happyhacker
    我还以为只有我自己觉得文风很怪,尤其被朗读者读出来的时候听着更难受了。不过文章的细节很到位。刚买了本作者的Go并发编程实战

    作者回复: 谢谢惠顾。写作技巧持续增进中。

    2018-10-26
    2
  • Ye
    老师您好,针对jenningsloy318代码的问题我做了些修改。
    先创建个test5文件夹,把main.go放进去,在test5文件夹里面建个package1文件夹作为放置"package1" 包的路径,并将func1.go放入package1文件夹里。
    代码如下
    main.go

    package main
    import "test5/package1"
    var amv = 123
    func main(){
         amv := 456
         package1.Printa(amv)
    }

    --------------------
    func1.go

    package package1
    import "fmt"
    func Printa(input int){
        fmt.Println(input)
    }

    编译成功,输出结果456。这里思路main文件想通过条用package1包里面的Printa来打印main文件里面的amv变量,在调用Printa时候创建了新的作用域。但和main作用域分别属于不同的包,所以Printa不能共享main 里面的变量或者全域变量,所以必须给Printa手动添加amv作为输入值。另外,如果我删除func main 里面的 amv :=456,package1.Printa()找不到amv值,所以就会去找代码块外的amv,也就是 package main 里面全域变量的amv,然后输出结果就是123了,我的理解对吗?
    2018-10-04
    2
  • Sky
    import . XXX
    应该是 import . "XXX"

    作者回复: 好吧,貌似"XXX"更一致一些。谢谢。

    2019-06-04
    1
  • 伟-Vae
    你好老师,请看代码
    var result int
    switch(op){
    case 1:
            result = 1
    break
    case 2:
            result =2
    break
    ......
    }
    fmt.println(result)

    switch内属于一个代码块,里面对result赋值,怎么会改变外部result的值

    作者回复: 因为它们操作的是同一个变量啊。你可以在case里用 := 给 result 赋值再看看。

    2019-04-11
    1
  • 言十年
    我觉得用了变量就尽量用一个类型。用一个变量代表不同类型的,我觉得算是编码粗糙了。
    隐晦的问题。不仅是可重声明变量。下面代码也会。

    ```
    package main

    var name = "野原新之助"
    func main() {
    var name = 1
    print(name)
    }
    ```

     

    作者回复: 小新,你好。这确实是需要避免的。

    2019-01-07
    1
  • 蛋蛋鸡
    ”可重命名变量“这个词听起来容易让人误解。初学者可能会认为这是变量的特性,但其实是作用域的问题,外围变量被隐藏了。当然,关于这个问题作者已经在文章中说得很清楚了,这里只是指出对于这个名词的顾虑。有C++经验的同学可能会比较有感触,用作用域操作符(::)可以把全局变量找出来。
    2018-11-17
    1
收起评论
45
返回
顶部