1. 作用域
标识符的可见性。主要是变量,常量。当定义好标识符(变量)后,在什么位置可以使用。就是作用域 scope 的问题。
作用域主要点:
- 块级作用域。在语句块中声明的变量,仅仅在该语句块中有效。语句块,{} 定义
的就是语句块,包括函数,if, swtich,for, for..range 语句结构,和独立的大括号。 - 作用域是嵌套的。内层作用域可以访问外层作用域的标识符(变量)。例如函数中 if 语句块,可以访问函数中定义的变量。若出现内外层重名的标识符,优先访问所 在作用域的标识符。
- 定义的变量,仅可以在当前块及其内部块访问到。
- 定义在函数外的标识符称之为全局标识符。全局,仅可以在当前包中访问,其他包
中需要通过包名来引用才可以访问。
2.闭包现象
closure
当作用域是嵌套情况下时(go 语言就是嵌套的),内层作用域(语句块)使用外层作用域的变量,当外层语句块运行结束后,如果内层语句块还可以被使用,则内层语句块与外层 变量构成了闭包。闭包现象会导致,外层语句中定义的变量,不会立即被释放。
演示:
func outer() func() {
n := 42
return func() {
n ++ // 使用外层变量
fmt.Println(n)
}
}
f := outer()
// outer()运行结束,外层语句块
f() // 42
上面的代码中,内层函数就与外层 outer()定义的变量 n 形成了闭包,n 不会随着 outer()的运 行结束而释放。
Go 语言支持闭包!没有好坏!
闭包,将一组数据和一些操作组合在了一起,也被称作带有数据的操作。(面向对象编程,
带有操作的数据),体现了数据的实体性。
由于闭包可以将数据和操作绑定在一起,完成一些特定的封装业务逻辑,演示做一个计数器, 使用全局变量的方案:
// 全局变量 counter 用于计数
var counter = 0
func CounterIncr() {
counter ++
}
func CounterDecr() {
counter --
}
该方案致命的问题是,counter 这个变量,不仅仅 CounterIncr() CunterDecr()可用,任何代码 都可用,不能保证 couter 变量的准确性。如何才能将 counter 与具体的操作函数绑定在一起 呢?只能某些个函数可以操作 counter。
利用闭包来实现,利用闭包来实现封装的计数器。
func CounterInit(initValue int) (func(), func(), func() int) {
counter := initValue
return func() {
counter ++
}, func() {
counter --
}, func() int {
return counter
}
}
CounterIncr, CounterDecr, CounterGetter := CounterInit(0)
fmt.Println(CounterGetter())
CounterIncr()
CounterIncr()
CounterDecr()
fmt.Println(CounterGetter())