Go语言核心之美 1.5-作用域

变量的作用域是指程序代码中能够有效使用这个变量的范围。

不要将作用域和生命期混在一起。

作用域是代码中的一块区域,是一个编译期的属性;生命期是程序执行期间变量存活的时间段。在此时间段内,变量能够被程序的其他部分所引用,是执行期的概念。


语法块是包括在花括号内的一系列语句。比如函数体或者循环体。语法块内部声明的变量是无法被语法块外部代码訪问的。我们能够扩展局部语法块的概念,在某些场景下。是不须要花括号的,这样的形式称之为词法块。

词法块分为几种:全局词法块,包括全部源码。包 词法块。包括整个package;文件词法块。包括整个文件;for、if、switch语句的词法块;switch或select中的case分支的词法块。当然也包括之前提到的语法块

声明语句的词法块决定了变量的作用域。

Go语言的内置类型、内置函数、内置常量都是全局词法块,因此它们都是全局作用域的。比如int、len、true等。能够在整个程序直接使用;对于导入的包。比如temconv导入的fmt包,是文件词法块,因此仅仅能在当前文件里訪问fmt包。这里fmt是全文件范围的作用域;tempconv.CToF函数中的变量c,则是局部词法块(语法块)的,因此它的作用域是函数的内部。

控制语句后面的标签(label)。比如break、continue或goto后的标签。它们的作用域是在控制语句所在的函数内部。



一个程序可能会有多个同样的变量名,仅仅要它们的声明在不同的词法块就好。

比如。你能够在函数内声明一个局部变量x,同一时候再声明一个包级的变量x,这是在函数内部,局部变量x就会替代后者,这里称之为shadow,意味着在函数作用域内局部变量将包变量隐藏了。

当编译器遇到一个变量名的引用时,会去搜索该变量的声明语句,首先从最内部的词法块開始,然后直到全局词法块。假设编译期找不到变量名的声明语句。那么就会报错:undeclared name。假设变量名在内部的词法块和外部的词法块同一时候声明,那么依据编译期的搜索规则,内部的词法块会先找到。

在这样的情况下。内部的声明会隐藏外部的声明(shadow),此时。外部声明的变量是无法訪问的:

func f() {}var g = "g"func main() {f := "f"fmt.Println(f) // "f"; 本地变量f隐藏了包级函数ffmt.Println(g) // "g"; 包级变量gfmt.Println(h) // compile error: undefined: h
}




在函数内部。词法块能够嵌套随意层数,因此本地变量能够隐藏外部变量。大多数的语法块(花括号)是通过控制语句if、for等创建的,以下的程序有三个不同的x变量。每一个变量都是声明在不同的词法块中(这段代码主要是为了说明作用域的规则,这样的编码风格并不提倡!):

func main() {x := "hello!"for i := 0; i < len(x); i++ {x := x[i]if x != '!' {x := x + 'A' - 'a'fmt.Printf("%c", x) // "HELLO" (one letter per iteration)}}
}
表达式x[i]和x + 'A' - 'a' 分别引用了不同的x变量,后面会解释。

就像之前提到的那样,不是全部的词法块都有显式的花括号。上面的for循环创建了两个词法块:带花括号的循环主体,显式词法块;还有不带花括号的隐式词法块。比如for循环的条件语句中声明一个变量i。这里i的作用域包括for的条件语句和for的主体。



以下的样例也创建了三个变量x,每一个都在不同的词法块中声明,一个在函数主体中,一个在for的隐式词法块中,另一个在显式词法块-循环主体中,这当中仅仅有1和3是显式词法块:

func main() {x := "hello"for _, x := range x {x := x + 'A' - 'a'fmt.Printf("%c", x) // "HELLO" (每次循环一个字符)}
}
就像for循环一样。if语句和switch语句一样会创建隐式词法块。以下的代码在if-else链中说明了x和y的作用域:
if x := f(); x == 0 {fmt.Println(x)
} else if y := g(x); x == y {fmt.Println(x, y)
} else {fmt.Println(x, y)
}
fmt.Println(x, y) // compile error: x and y are not visible here

第二个if语句嵌套在第一个里面。所以第一个if语句里声明的变量对第二个if语句是可见的。在switch中也有相似的规则:除了条件词法块外。每一个case也有自己的词法块。


对于包级变量来说,声明的顺序和作用域是无关的,全部一个包级变量声明时能够引用它自身也能够引用在它之后声明的包级变量。然而,假设一个变量或者常量在声明时引用了它自己。编译器会报错。



看以下的程序:

if f, err := os.Open(fname); err != nil { // compile error: unused: freturn err
}
f.ReadByte() // compile error: undefined f
f.Close()    // compile error: undefined f
f的作用域仅仅是if语句,因此在if之外的词法块是不可訪问的,报编译错误。

这里能够更改代码,提前声明f变量:

f, err := os.Open(fname)
if err != nil {return err
}
f.ReadByte()
f.Close()
假设不想在外部词法块声明变量,能够这么写:
if f, err := os.Open(fname); err != nil {return err
} else {// f and err are visible here toof.ReadByte()f.Close()
}
可是第三种不是Go推荐的写法。另外一种比較合适,将正常逻辑和错误处理分离。



短声明变量的作用域是要特别注意的,考虑以下的程序,開始时会获取当前的工作文件夹,保存在一个包级变量中。

这个本来能够通过在main函数中调用os.Getwd来完毕,可是用init函数将这块儿逻辑从主逻辑中分离是一个更好的选择,特别是由于获取文件夹的操作可能会是失败的,这个时候须要处理返回的错误。函数log.Fatalf会打印一条信息。然后调用os.Exit(1)终结程序:

var cwd stringfunc init() {cwd, err := os.Getwd() // compile error: unused: cwdif err != nil {log.Fatalf("os.Getwd failed: %v", err)}
}
这里cwd和err在init的词法块中都没有声明过,因此 := 语句会将它们两声明为本地变量。init内部的cwd声明会隐藏外部的,因此这个程序没有达到更新包级变量cwd的目的。

当前版本号,Go编译器会检測到本地变量cwd从未使用,因此会报错,可是这样的检查并非非常严格。比如。假设在log.Fatalf中打印cwd的值(这时本地变量cwd会被使用)。那么这样的错误就会被隐藏!!

var cwd stringfunc init() {cwd, err := os.Getwd() // 注意这里的包级cwd被本地隐藏了,可是编译器没有报错!if err != nil {log.Fatalf("os.Getwd failed: %v", err)}log.Printf("Working directory = %s", cwd)
}
这里全局变量cwd没有得到正确的初始化,同一时候,log函数使用了cwd本地变量。隐藏了这个bug。

有一些办法能够处理这样的潜在的错误,最直接的就是避免使用:=,通过var来声明err变量:

var cwd stringfunc init() {var err errorcwd, err = os.Getwd()if err != nil {log.Fatalf("os.Getwd failed: %v", err)}
}


在这一章里,我们简单学习了包。文件。声明。语句等。

在接下来的两章,我们会学习数据结构




PS. 这一章真心非常难写,足足用了3个小时。作为质量对照大家能够參见这篇文章Scope。




文章全部权:Golang隐修会 联系人:孙飞。CTO@188.com!


转载于:https://www.cnblogs.com/llguanli/p/8378254.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/368074.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

BootStrap_01之全局样式

1、响应式网页&#xff1a; ①Responsive Web Page&#xff1a;一个可以根据浏览设备的不同&#xff0c;而自动更改布局、图片、文字效果的网页&#xff1b; ②构成&#xff1a;不能固定宽度&#xff0c;必须流式布局&#xff1b;文字和图片大小随容器大小而改变&#xff1b;CS…

Java安全教程–创建SSL连接和证书的分步指南

在有关应用JEE安全性的系列文章中&#xff0c;我们为您提供了另一个有关如何在Java EE应用程序中创建SSL连接和创建证书的详细教程。 如我们之前的文章中所述&#xff0c; 安全套接字层&#xff08;SSL&#xff09;/传输层安全性&#xff08;TLS&#xff09;将启用客户端和Web服…

linux带宽最小的远程桌面,【图片】linux下哪种远程桌面服务最快?_linux吧_百度贴吧...

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼I stumbled upon this while researching xrdp, which is the only one you mentioned that I havent used. Some observations:NX Server: not so speedy (v3, still awaiting v4), might pose performance issues depending on y…

CentOS Vi编辑器

vim&#xff1a;通过vim a.cfg进入文档 i&#xff1a;编辑状态 ESC&#xff1a;返回不可编辑状态 dd&#xff1a;在不可编辑状态下&#xff0c;dd可删除光标所在的行&#xff0c;2dd删除两行&#xff0c;以此类推 u&#xff1a;在不可编辑状态下&#xff0c;u可恢复删除的行 yy…

[转]CSS hack大全详解

转自&#xff1a;CSS hack大全&详解 1、什么是CSS hack? CSS hack是通过在CSS样式中加入一些特殊的符号&#xff0c;让不同的浏览器识别不同的符号&#xff08;什么样的浏览器识别什么样的符号是有标准的&#xff0c;CSS hack就是让你记住这个标准&#xff09;&#xff0c…

摆脱困境:将环境特定的Cron表达式与@Scheduled批注一起使用

Scheduled注释提供了一种在Spring驱动的应用程序中创建计划任务的简便方法。 我们可以使用它通过定期调度或cron表达式来调度我们的任务。 尽管时段调度也可能有用&#xff0c;但是cron表达式使我们对调度任务的调用有了更多的控制。 这就是为什么它们在现实生活中非常有用的原…

linux编译的so android闪退,android so库导致的闪退及tombstone分析

android中有3种crash情况&#xff1a;未捕获的异常、ANR和闪退。未捕获的异常一般用crash文件就可以记录异常信息&#xff0c;而ANR无响应表现就是界面卡着无法响应用户操作&#xff0c;而闪退则是整个app瞬间退出&#xff0c;个人感觉对用户造成的体验最差。闪退一般是由于调用…

产品经理应该具备的技能(2)如何做一个好的数据产品经理?

一、如何做一个好的数据产品经理&#xff1f; PD&#xff08;指产品经理&#xff0c;下同&#xff09;本身就是在做牛做马&#xff0c;关系圈异常复杂。数据PD也不例外。而且打交道的人更多。以下是我用PPT绘制的数据产品经理关系圈。如果你也做过 数据产品的产品经理&#xff…

C#取整函数Math.Round、Math.Ceiling和Math.Floor 【非原创,用来收藏,分享】

1.Math.Round&#xff1a;四舍六入五取偶 引用内容Math.Round(0.0) //0Math.Round(0.1) //0Math.Round(0.2) //0Math.Round(0.3) //0Math.Round(0.4) //0Math.Round(0.5) //0Math.Round(0.6) //1Math.Round(0.7) //1Math.Round(0.8) //1Math.Round(0.9) //1说明&#xff1a;对于…

谈谈一些有趣的CSS题目(十)-- 结构性伪类选择器

开本系列&#xff0c;谈谈一些有趣的 CSS 题目&#xff0c;题目类型天马行空&#xff0c;想到什么说什么&#xff0c;不仅为了拓宽一下解决问题的思路&#xff0c;更涉及一些容易忽视的 CSS 细节。解题不考虑兼容性&#xff0c;题目天马行空&#xff0c;想到什么说什么&#x…

基于linux的netfilter处理数据包的过程分析,基于Netfilter的网络数据包分析

前面的几篇文章我已经对Netfilter的大概的机制作了比较详细的介绍&#xff0c;这篇文章我就说一下如何分析网络数据包。我刚刚写了一个程序&#xff0c;程序的功能很简单&#xff0c;就是提取出网络数据包的源地址和改包所使用的网络协议&#xff0c;大家可以看看源代码&#x…

Java EE CDI程序化依赖关系消歧示例–注入点检查

在本教程中&#xff0c;我们将看到在注入Java EE CDI bean时如何避免程序依赖消除歧义。 我们已经在Jave EE依赖关系消除歧义示例中展示了如何避免CDI Bean中的依赖关系歧义消除。 在这里&#xff0c;我们将向您展示如何以动态方式避免依赖消除歧义。 我们将通过检查注入另一个…

机器学习算法整理(四)集成算法—随机森林模型

随机&#xff1a;数据采样随机&#xff0c;特征选择随机 &#xff08;数据采样&#xff0c;有放回&#xff09; 转载于:https://www.cnblogs.com/douzujun/p/8386930.html

CSS 布局入门

概述 Web 兴起之后&#xff0c;关于CSS的介绍和学习资料已经铺天盖地。 本文不涉及具体的CSS语法之类的&#xff0c;而是希望从初学者的角度&#xff0c;让没有接触或很少接触CSS的人能快速的了解 CSS 到底是什么以及如何使用。 什么是 CSS 了解一个概念&#xff0c;首先看到…

迷你搜索引擎–使用Neo4j,Crawler4j,Graphstream和Encog的基础知识

继续执行正在实现搜索引擎的Programming Collection Intelligence &#xff08;PCI&#xff09;的第4章。 我可能比做一次运动所咬的东西要多。 我认为&#xff0c; 与其一直使用本书中使用的普通关系数据库结构&#xff0c;不如说我一直想看看Neo4J&#xff0c;所以现在是时候…

linux人脸识别视频推流,RTMP推流协议视频智能分析/人脸识别/直播点播平台EasyDSS接口调用注意事项介绍...

TSINGSEE青犀视频目前推出了前端支持不同协议设备接入的视频智能分析平台&#xff0c;包括RTSP协议的EasyNVR、GB28181协议的EasyGBS&#xff0c;RTMP推流协议的EasyDSS&#xff0c;还有能够进行人脸识别、车牌识别的EasyCVR&#xff0c;这些平台均提供了视频转码分发的能力&am…

js高级写法

名称 一般写法优化取整(不四舍五入) parseInt(a,10); //Before Math.floor(a); //Before a>>0; //Before ~~a; //After a|0; //After 取整(四舍五入&#xff09; Math.round(a); //Beforenum.toFixed(0) a.5|0; //After未定义 undefined; //Before void 0; //After, 快…

IntersectionObserve API使用

why 之前图片懒加载的实现方法大多数为&#xff1a;给window添加scroll事件&#xff0c;滚动时获取元素的offset值&#xff0c;判断元素在viewport内的可见行。这样做的缺点是&#xff1a;频繁的计算dom节点的属性导致性能较差&#xff0c;对scroll绑定的回调函数进行节流能减少…

css小技巧(1)

1、-webkit-overflow-scrolling: touch; 解决ios滑动时无缓冲问题 2、::-webkit-scrollbar 设置ios滑动时是否显示滚动条 3、::selection 选中文字时文字颜色和选中色 <!doctype html> <html> <head> <meta charset"utf-8"> <meta cont…

在基于图论的Java程序中基于DSL的输入图数据的方法

我们大多数人已经编写了一些程序来处理图论算法&#xff0c;例如查找两个顶点之间的最短路径&#xff0c;查找给定图的最小生成树等等。 在这些算法的每一种中&#xff0c;表示图形的编程方式是使用邻接矩阵或邻接表 。 两者都不是定义图形输入的非常直观的方法。 例如&#xf…