### 閉包
閉包是由函數及其相關引用環境組合而成的實體(即:閉包=函數+引用環境)
“官方”的解釋是:所謂“閉包”,指的是一個擁有許多變量和綁定了這些變量的環境的表達式(通常是一個函數),因而這些變量也是該表達式的一部分
javascript閉包示例:
~~~
<script>
function a(){
var i=0;
function b(){
console.log(++i);
document.write("<h1>"+i+"</h1>");
}
return b;
}
$(function(){
var c=a();
c();
c();
c();
//a(); //不會有信息輸出
document.write("<h1>=============</h1>");
var c2=a();
c2();
c2();
});
</script>
~~~
這段代碼有兩個特點:
函數b嵌套在函數a內部 函數a返回函數b 這樣在執行完var c=a()后,變量c實際上是指向了函數b(),再執行函數c()后就會顯示i的值,第一次為1,第二次為2,第三次為3,以此類推。 其實,這段代碼就創建了一個閉包。因為函數a()外的變量c引用了函數a()內的函數b(),就是說:
當函數a()的內部函數b()被函數a()外的一個變量引用的時候,就創建了一個閉包。 在上面的例子中,由于閉包的存在使得函數a()返回后,a中的i始終存在,這樣每次執行c(),i都是自加1后的值。 從上面可以看出閉包的作用就是在a()執行完并返回后,閉包使得Javascript的垃圾回收機制GC不會收回a()所占用的資源,因為a()的內部函數b()的執行需要依賴a()中的變量i。
在給定函數被多次調用的過程中,這些私有變量能夠保持其持久性。變量的作用域僅限于包含它們的函數,因此無法從其它程序代碼部分進行訪問。不過,變量的生存期是可以很長,在一次函數調用期間所創建所生成的值在下次函數調用時仍然存在。正因為這一特點,閉包可以用來完成信息隱藏,并進而應用于需要狀態表達的某些編程范型中。 下面來想象另一種情況,如果a()返回的不是函數b(),情況就完全不同了。因為a()執行完后,b()沒有被返回給a()的外界,只是被a()所引用,而此時a()也只會被b()引 用,因此函數a()和b()互相引用但又不被外界打擾(被外界引用),函數a和b就會被GC回收。所以直接調用a();是頁面并沒有信息輸出。
下面來說閉包的另一要素引用環境。c()跟c2()引用的是不同的環境,在調用i++時修改的不是同一個i,因此兩次的輸出都是1。函數a()每進入一次,就形成了一個新的環境,對應的閉包中,函數都是同一個函數,環境卻是引用不同的環境。這和c()和c()的調用順序都是無關的。
### Go的閉包
Go語言是支持閉包的,這里只是簡單地講一下在Go語言中閉包是如何實現的。 下面我來將之前的JavaScript的閉包例子用Go來實現。
~~~
package main
import (
"fmt"
)
func a() func() int {
i := 0
b := func() int {
i++
fmt.Println(i)
return i
}
return b
}
func main() {
c := a()
c()
c()
c()
a() //不會輸出i
}
~~~
輸出結果:
~~~
1
2
3
~~~
可以發現,輸出和之前的JavaScript的代碼是一致的。具體的原因和上面的也是一樣的,這說明Go語言是支持閉包的。
閉包復制的是原對象指針,這就很容易解釋延遲引用現象。
~~~
package main
import "fmt"
func test() func() {
x := 100
fmt.Printf("x (%p) = %d\n", &x, x)
return func() {
fmt.Printf("x (%p) = %d\n", &x, x)
}
}
func main() {
f := test()
f()
}
~~~
輸出:
~~~
x (0xc42007c008) = 100
x (0xc42007c008) = 100
~~~
在匯編層 ,test 實際返回的是 FuncVal 對象,其中包含了匿名函數地址、閉包對象指針。當調 匿名函數時,只需以某個寄存器傳遞該對象即可。
~~~
FuncVal { func_address, closure_var_pointer ... }
~~~
外部引用函數參數局部變量
~~~
package main
import "fmt"
// 外部引用函數參數局部變量
func add(base int) func(int) int {
return func(i int) int {
base += i
return base
}
}
func main() {
tmp1 := add(10)
fmt.Println(tmp1(1), tmp1(2))
// 此時tmp1和tmp2不是一個實體了
tmp2 := add(100)
fmt.Println(tmp2(1), tmp2(2))
}
~~~
返回2個閉包
~~~
package main
import "fmt"
// 返回2個函數類型的返回值
func test01(base int) (func(int) int, func(int) int) {
// 定義2個函數,并返回
// 相加
add := func(i int) int {
base += i
return base
}
// 相減
sub := func(i int) int {
base -= i
return base
}
// 返回
return add, sub
}
func main() {
f1, f2 := test01(10)
// base一直是沒有消
fmt.Println(f1(1), f2(2))
// 此時base是9
fmt.Println(f1(3), f2(4))
}
~~~
- 概述
- go語言基礎特性
- Go語言聲明
- Go項目構建及編譯
- go command
- 程序設計原則
- Go基礎
- 變量
- 常量
- iota
- 基本類型
- byte和rune類型
- 類型定義和類型別名
- 數組
- string
- 高效字符串連接
- string底層原理
- 運算符
- new
- make
- 指針
- 下劃線 & import
- 語法糖
- 簡短變量申明
- 流程控制
- ifelse
- switch
- select
- select實現原理
- select常見案例
- for
- range
- range實現原理
- 常見案例
- range陷阱
- Goto&Break&Continue
- Go函數
- 函數
- 可變參數函數
- 高階函數
- init函數和main函數
- 匿名函數
- 閉包
- 常用內置函數
- defer
- defer常見案例
- defer規則
- defer與函數返回值
- defer實現原理
- defer陷阱
- 數據結構
- slice
- slice內存布局
- slice&array
- slice底層實現
- slice陷阱
- map
- Map實現原理
- 集合
- List
- Set
- 線程安全數據結構
- sync.Map
- Concurrent Map
- 面向對象編程
- struct
- 匿名結構體&匿名字段
- 嵌套結構體
- 結構體的“繼承”
- struct tag
- 行為方法
- 方法與函數
- type Method Value & Method Expressions
- interface
- 類型斷言
- 多態
- 錯誤機制
- error
- 自定義錯誤
- panic&recover
- reflect
- reflect包
- 應用示例
- DeepEqual
- 反射-fillObjectField
- 反射-copyObject
- IO
- 讀取文件
- 寫文件
- bufio
- ioutil
- Go網絡編程
- tcp
- tcp粘包
- udp
- HTTP
- http服務
- httprouter
- webSocket
- go并發編程
- Goroutine
- thread vs goroutine
- Goroutine任務取消
- 通過channel廣播實現
- Context
- Goroutine調度機制
- goroutine調度器1.0
- GMP模型調度器
- 調度器竊取策略
- 調度器的生命周期
- 調度過程全解析
- channel
- 無緩沖的通道
- 緩沖信道
- 單向信道
- chan實現原理
- 共享內存并發機制
- mutex互斥鎖
- mutex
- mutex原理
- mutex模式
- RWLock
- 使用信道處理競態條件
- WaitGroup
- 工作池
- 并發任務
- once運行一次
- 僅需任意任務完成
- 所有任務完成
- 對象池
- 定時器Timer
- Timer
- Timer實現原理
- 周期性定時器Ticker
- Ticker對外接口
- ticker使用場景
- ticker實現原理
- ticker使用陷阱
- 包和依賴管理
- package
- 依賴管理
- 測試
- 單元測試
- 表格測試法
- Banchmark
- BDD
- 常用架構模式
- Pipe-filter pattern
- Micro Kernel
- JSON
- json-內置解析器
- easyjson
- 性能分析
- gc
- 工具類
- fmt
- Time
- builtin
- unsafe
- sync.pool
- atomic
- flag
- runtime
- strconv
- template