Go 函数,包(二)
生活随笔
收集整理的這篇文章主要介紹了
Go 函数,包(二)
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
#### Go 函數(shù),包(二)
***百丈峰,松如浪,地勢坤,厚德載物之像***
今天又到周五啦,你們有沒有激動呢,反正我很激動,又有兩天的自由了;
上一節(jié)我們學(xué)習(xí)了Go 的函數(shù)和包的一些知識 , 今天接著學(xué)習(xí)...
---
##### init 函數(shù)
每個程序源文件都可以包含一個init 函數(shù), 該函數(shù)在main 函數(shù)前執(zhí)行,被Go 運(yùn)行框架調(diào)用; package mainimport "fmt"func init(){fmt.Print("init ") } func main() {fmt.Print("main") }
輸出結(jié)果: init main
init 函數(shù)的注意事項和細(xì)節(jié):
1. 如果一個文件同時包含***全局變量定義,init函數(shù)和main 函數(shù)***則執(zhí)行的流程為全局變量定義->init函數(shù)->main函數(shù);
2. 其主要作用是完成一些初始化工作
3. 如果多個包都有init 函數(shù)時,執(zhí)行順序?yàn)閷?dǎo)入順序,如:
A 導(dǎo)入了 B , B 導(dǎo)入了 C , 那么執(zhí)行順序?yàn)橄葓?zhí)行C 中的全局變量定義,init 函數(shù),再執(zhí)行B 包中的, 最后執(zhí)行A 包中的; package mainimport "fmt" var a = test01() func init(){fmt.Println("init") // 執(zhí)行順序2 fmt.Println(a) // 執(zhí)行順序3 } func test01() int {fmt.Println("test01") // 執(zhí)行順序1return 1 } func main() {fmt.Println("main") // 執(zhí)行順序4 }
---
##### 匿名函數(shù)
Go 支持匿名函數(shù),匿名函數(shù)也就是沒有名字的函數(shù),如果某一個函數(shù)希望使用一次,可以使用匿名函數(shù),同時匿名函數(shù)也可以實(shí)現(xiàn)多次調(diào)用;
如果將匿名函數(shù)賦值給一個全局變量,那么這個匿名函數(shù)就成為了一個全局匿名函數(shù),在程序的有效作用域都有效;
package mainimport "fmt"func main(){// 定義時直接調(diào)用sum := func(a,b int) int {return a +b}(10,20)fmt.Println(sum)// 將匿名函數(shù)賦值給一個變量,通過變量調(diào)用匿名函數(shù)a := func(a,b int) int {return a+b}sum = a(10,20)fmt.Println(sum) }
---
##### 閉包
閉包實(shí)際上就是一個函數(shù)和與其相關(guān)的引用環(huán)境組合的一個整體 package mainimport "fmt"func add()func(int)int{var n int = 1return func(i int) int {n += ireturn n} } func main(){a := add()fmt.Println(a(1)) // 2fmt.Println(a(10)) //12fmt.Println(a(2)) //14 }
說明:
1. add 是一個函數(shù),返回的數(shù)據(jù)類型是一個匿名函數(shù);
2. 返回的匿名函數(shù) 引用到add 函數(shù)的變量n ,因此返回的匿名函數(shù)就和n 形成一個整體,構(gòu)成閉包;
3. 也可以這樣理解: 閉包是類,函數(shù)是方法,n 是字段,函數(shù)和它使用的n 構(gòu)成閉包;
4. 因此當(dāng)反復(fù)調(diào)用a 函數(shù)時, n 將累計; package mainimport ("fmt""strings" )func addStr() func(str string) string {var base = "a"return func(str string) string {base += strreturn base} } // 判斷字符串的后綴是否為指定的 func makeSuffix(suffix string) func(string) string {return func(s string) string {if !strings.HasSuffix(s,suffix) {return s + suffix}return s} } func main(){a := addStr()fmt.Println(a("b")) // abfmt.Println(a("c")) // abcfmt.Println(a("d")) // abcdjpg := makeSuffix(".jpg")png := makeSuffix(".png")fmt.Println(jpg("a.jpg")) // a.jpgfmt.Println(jpg("b")) //b.jpgfmt.Println(png("a.png")) // a.pngfmt.Println(png("b")) //b.png }
1. 返回的匿名函數(shù)和參數(shù)suffix 變量組合成一個閉包,因此傳入一次可以多次調(diào)用;
---
##### defer 函數(shù)
defer 也稱為延時執(zhí)行;
當(dāng)函數(shù)執(zhí)行到defer 時,暫時不執(zhí)行,將defer 后的語句壓入獨(dú)立的棧,當(dāng)函數(shù)執(zhí)行完畢,return 前,再從defer 棧按照先入后出原則方式執(zhí)行; package mainimport "fmt"func add(a,b int) int {defer fmt.Println("a=",a) //10 輸出順序3 , 在將語句放入棧時,相關(guān)參數(shù)的值會進(jìn)行拷貝defer fmt.Println("b=",b) //20 輸出順序2 , 在將語句放入棧時,相關(guān)參數(shù)的值會進(jìn)行拷貝a = 11sum := a + bdefer fmt.Println("sum=",sum) //31 輸出順序1return sum } func main(){sum := add(10,20)fmt.Println(sum) //31 輸出順序4 }
***在defer 將語句入到棧時,會將相關(guān)參數(shù)的值進(jìn)行拷貝***
1. 在Go 開發(fā)中通常會在創(chuàng)建資源(打開文件,連接數(shù)據(jù)庫,鎖資源)后使用defer file.Close() conn.Close()
2. 在defer 后,仍可以繼續(xù)使用已經(jīng)創(chuàng)建的資源;
---
##### 函數(shù)參數(shù)傳遞方式
上一節(jié)中已經(jīng)講過函數(shù)參數(shù)的值類型與引用類型,我們再來深入總結(jié)一下,這個知識點(diǎn)在編譯型語言中很重要;
1. 函數(shù)參數(shù)是值類型時就是值傳遞,函數(shù)參數(shù)是引用類型就是引用傳遞;
2. 不管是值傳遞還是引用傳遞,傳遞給函數(shù)的都是變量的副本,不同的是值傳遞是值的拷貝,引用傳遞是地址的拷貝;
3. 一般情況下地址拷貝效率高,而值拷貝由參數(shù)的數(shù)據(jù)大小, 數(shù)據(jù)越大,效率越低;
4. 值類型: 基本數(shù)據(jù)類型int 系列, float 系列, bool, string, 數(shù)組,結(jié)構(gòu)體;
5. 引用類型: 指針,slice 切片, map , chan ,interface ; package mainimport "fmt"// 值傳遞 func test01(a int){fmt.Printf("[test01] a value= %d a address=%p\n",a,&a) } // 對于值傳遞希望更改原來的值可以傳入變量地址 func test03(a *int){*a = 100fmt.Printf("[test03] c value=%v c address=%p\n",*a,&a) } // 引用傳遞 func test02(a []int){fmt.Printf("[test02] b value=%v b address=%p\n",a,&a) } func main(){var a int = 10fmt.Printf("a value= %d a address=%p\n",a,&a)test01(a)var b = []int{1,2}fmt.Printf("b value=%v b address=%p\n",b,&b)test02(b)var c = 10test03(&c)fmt.Printf("c value=%v c address=%p\n",c,&c) }
---
##### 變量的作用域
1. 函數(shù)內(nèi)部聲明或定義的變量是局部變量,作用域限于函數(shù)內(nèi)部;
2. 函數(shù)外部聲明或定義的變量是全局變量,作用域在整個包有效,若首字母大寫,則作用域?yàn)檎麄€程序;
3. 如果變量聲明或定義在代碼塊內(nèi),如: if/for 代碼塊內(nèi),則作用域僅限于代碼塊; package mainimport "fmt"// 全局變量 var name = "golang" // 如果首字母大寫,則在整個程序中有效,其它包也可以使用 var Age = 22func test01(){// 局部變量var a int = 10fmt.Println(a)// 使用全局變量fmt.Println(name)if a > 2 {// 代碼塊內(nèi)部的局部變量, 僅限于if 代碼塊有效var d int = 100fmt.Println(d)}// 代碼塊內(nèi)部的局部變量, 僅限于if 代碼塊有效//fmt.Println(d) // error } func main(){// 局部變量var b int = 1fmt.Println(b)// 使用全局變量fmt.Println(name)test01() }
***百丈峰,松如浪,地勢坤,厚德載物之像***
今天又到周五啦,你們有沒有激動呢,反正我很激動,又有兩天的自由了;
上一節(jié)我們學(xué)習(xí)了Go 的函數(shù)和包的一些知識 , 今天接著學(xué)習(xí)...
---
##### init 函數(shù)
每個程序源文件都可以包含一個init 函數(shù), 該函數(shù)在main 函數(shù)前執(zhí)行,被Go 運(yùn)行框架調(diào)用; package mainimport "fmt"func init(){fmt.Print("init ") } func main() {fmt.Print("main") }
輸出結(jié)果: init main
init 函數(shù)的注意事項和細(xì)節(jié):
1. 如果一個文件同時包含***全局變量定義,init函數(shù)和main 函數(shù)***則執(zhí)行的流程為全局變量定義->init函數(shù)->main函數(shù);
2. 其主要作用是完成一些初始化工作
3. 如果多個包都有init 函數(shù)時,執(zhí)行順序?yàn)閷?dǎo)入順序,如:
A 導(dǎo)入了 B , B 導(dǎo)入了 C , 那么執(zhí)行順序?yàn)橄葓?zhí)行C 中的全局變量定義,init 函數(shù),再執(zhí)行B 包中的, 最后執(zhí)行A 包中的; package mainimport "fmt" var a = test01() func init(){fmt.Println("init") // 執(zhí)行順序2 fmt.Println(a) // 執(zhí)行順序3 } func test01() int {fmt.Println("test01") // 執(zhí)行順序1return 1 } func main() {fmt.Println("main") // 執(zhí)行順序4 }
---
##### 匿名函數(shù)
Go 支持匿名函數(shù),匿名函數(shù)也就是沒有名字的函數(shù),如果某一個函數(shù)希望使用一次,可以使用匿名函數(shù),同時匿名函數(shù)也可以實(shí)現(xiàn)多次調(diào)用;
如果將匿名函數(shù)賦值給一個全局變量,那么這個匿名函數(shù)就成為了一個全局匿名函數(shù),在程序的有效作用域都有效;
package mainimport "fmt"func main(){// 定義時直接調(diào)用sum := func(a,b int) int {return a +b}(10,20)fmt.Println(sum)// 將匿名函數(shù)賦值給一個變量,通過變量調(diào)用匿名函數(shù)a := func(a,b int) int {return a+b}sum = a(10,20)fmt.Println(sum) }
---
##### 閉包
閉包實(shí)際上就是一個函數(shù)和與其相關(guān)的引用環(huán)境組合的一個整體 package mainimport "fmt"func add()func(int)int{var n int = 1return func(i int) int {n += ireturn n} } func main(){a := add()fmt.Println(a(1)) // 2fmt.Println(a(10)) //12fmt.Println(a(2)) //14 }
說明:
1. add 是一個函數(shù),返回的數(shù)據(jù)類型是一個匿名函數(shù);
2. 返回的匿名函數(shù) 引用到add 函數(shù)的變量n ,因此返回的匿名函數(shù)就和n 形成一個整體,構(gòu)成閉包;
3. 也可以這樣理解: 閉包是類,函數(shù)是方法,n 是字段,函數(shù)和它使用的n 構(gòu)成閉包;
4. 因此當(dāng)反復(fù)調(diào)用a 函數(shù)時, n 將累計; package mainimport ("fmt""strings" )func addStr() func(str string) string {var base = "a"return func(str string) string {base += strreturn base} } // 判斷字符串的后綴是否為指定的 func makeSuffix(suffix string) func(string) string {return func(s string) string {if !strings.HasSuffix(s,suffix) {return s + suffix}return s} } func main(){a := addStr()fmt.Println(a("b")) // abfmt.Println(a("c")) // abcfmt.Println(a("d")) // abcdjpg := makeSuffix(".jpg")png := makeSuffix(".png")fmt.Println(jpg("a.jpg")) // a.jpgfmt.Println(jpg("b")) //b.jpgfmt.Println(png("a.png")) // a.pngfmt.Println(png("b")) //b.png }
1. 返回的匿名函數(shù)和參數(shù)suffix 變量組合成一個閉包,因此傳入一次可以多次調(diào)用;
---
##### defer 函數(shù)
defer 也稱為延時執(zhí)行;
當(dāng)函數(shù)執(zhí)行到defer 時,暫時不執(zhí)行,將defer 后的語句壓入獨(dú)立的棧,當(dāng)函數(shù)執(zhí)行完畢,return 前,再從defer 棧按照先入后出原則方式執(zhí)行; package mainimport "fmt"func add(a,b int) int {defer fmt.Println("a=",a) //10 輸出順序3 , 在將語句放入棧時,相關(guān)參數(shù)的值會進(jìn)行拷貝defer fmt.Println("b=",b) //20 輸出順序2 , 在將語句放入棧時,相關(guān)參數(shù)的值會進(jìn)行拷貝a = 11sum := a + bdefer fmt.Println("sum=",sum) //31 輸出順序1return sum } func main(){sum := add(10,20)fmt.Println(sum) //31 輸出順序4 }
***在defer 將語句入到棧時,會將相關(guān)參數(shù)的值進(jìn)行拷貝***
1. 在Go 開發(fā)中通常會在創(chuàng)建資源(打開文件,連接數(shù)據(jù)庫,鎖資源)后使用defer file.Close() conn.Close()
2. 在defer 后,仍可以繼續(xù)使用已經(jīng)創(chuàng)建的資源;
---
##### 函數(shù)參數(shù)傳遞方式
上一節(jié)中已經(jīng)講過函數(shù)參數(shù)的值類型與引用類型,我們再來深入總結(jié)一下,這個知識點(diǎn)在編譯型語言中很重要;
1. 函數(shù)參數(shù)是值類型時就是值傳遞,函數(shù)參數(shù)是引用類型就是引用傳遞;
2. 不管是值傳遞還是引用傳遞,傳遞給函數(shù)的都是變量的副本,不同的是值傳遞是值的拷貝,引用傳遞是地址的拷貝;
3. 一般情況下地址拷貝效率高,而值拷貝由參數(shù)的數(shù)據(jù)大小, 數(shù)據(jù)越大,效率越低;
4. 值類型: 基本數(shù)據(jù)類型int 系列, float 系列, bool, string, 數(shù)組,結(jié)構(gòu)體;
5. 引用類型: 指針,slice 切片, map , chan ,interface ; package mainimport "fmt"// 值傳遞 func test01(a int){fmt.Printf("[test01] a value= %d a address=%p\n",a,&a) } // 對于值傳遞希望更改原來的值可以傳入變量地址 func test03(a *int){*a = 100fmt.Printf("[test03] c value=%v c address=%p\n",*a,&a) } // 引用傳遞 func test02(a []int){fmt.Printf("[test02] b value=%v b address=%p\n",a,&a) } func main(){var a int = 10fmt.Printf("a value= %d a address=%p\n",a,&a)test01(a)var b = []int{1,2}fmt.Printf("b value=%v b address=%p\n",b,&b)test02(b)var c = 10test03(&c)fmt.Printf("c value=%v c address=%p\n",c,&c) }
---
##### 變量的作用域
1. 函數(shù)內(nèi)部聲明或定義的變量是局部變量,作用域限于函數(shù)內(nèi)部;
2. 函數(shù)外部聲明或定義的變量是全局變量,作用域在整個包有效,若首字母大寫,則作用域?yàn)檎麄€程序;
3. 如果變量聲明或定義在代碼塊內(nèi),如: if/for 代碼塊內(nèi),則作用域僅限于代碼塊; package mainimport "fmt"// 全局變量 var name = "golang" // 如果首字母大寫,則在整個程序中有效,其它包也可以使用 var Age = 22func test01(){// 局部變量var a int = 10fmt.Println(a)// 使用全局變量fmt.Println(name)if a > 2 {// 代碼塊內(nèi)部的局部變量, 僅限于if 代碼塊有效var d int = 100fmt.Println(d)}// 代碼塊內(nèi)部的局部變量, 僅限于if 代碼塊有效//fmt.Println(d) // error } func main(){// 局部變量var b int = 1fmt.Println(b)// 使用全局變量fmt.Println(name)test01() }
個人微信公眾號上有最新文章: 歡迎大家關(guān)注一同學(xué)習(xí)交流
轉(zhuǎn)載于:https://www.cnblogs.com/Mail-maomao/p/11411767.html
總結(jié)
以上是生活随笔為你收集整理的Go 函数,包(二)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。