• Go 函数,包(二)


    #### Go 函数,包(二)
    ***百丈峰,松如浪,地势坤,厚德载物之像***
    今天又到周五啦,你们有没有激动呢,反正我很激动,又有两天的自由了;
    上一节我们学习了Go 的函数和包的一些知识 , 今天接着学习...
    ---
    ##### init 函数
    每个程序源文件都可以包含一个init 函数, 该函数在main 函数前执行,被Go 运行框架调用;
    package main
    
    import "fmt"
    
    func init(){
       fmt.Print("init ")
    }
    func main()  {
       fmt.Print("main")
    }
    

      


    输出结果: init main
    init 函数的注意事项和细节:
    1. 如果一个文件同时包含***全局变量定义,init函数和main 函数***则执行的流程为全局变量定义->init函数->main函数;
    2. 其主要作用是完成一些初始化工作
    3. 如果多个包都有init 函数时,执行顺序为导入顺序,如:
    A 导入了 B , B 导入了 C , 那么执行顺序为先执行C 中的全局变量定义,init 函数,再执行B 包中的, 最后执行A 包中的;
    package main
    
    import "fmt"
    var a = test01()
    func init(){
       fmt.Println("init") // 执行顺序2 
       fmt.Println(a) // 执行顺序3
    }
    func test01() int {
       fmt.Println("test01") // 执行顺序1
       return 1
    }
    func main()  {
       fmt.Println("main") // 执行顺序4
    }
    

      


    ---
    ##### 匿名函数
    Go 支持匿名函数,匿名函数也就是没有名字的函数,如果某一个函数希望使用一次,可以使用匿名函数,同时匿名函数也可以实现多次调用;
    如果将匿名函数赋值给一个全局变量,那么这个匿名函数就成为了一个全局匿名函数,在程序的有效作用域都有效;

    package main
    
    import "fmt"
    
    func main(){
       // 定义时直接调用
       sum := func(a,b int) int {
          return a +b
       }(10,20)
       fmt.Println(sum)
       // 将匿名函数赋值给一个变量,通过变量调用匿名函数
       a := func(a,b int) int {
          return a+b
       }
       sum = a(10,20)
       fmt.Println(sum)
    }
    

      


    ---
    ##### 闭包
    闭包实际上就是一个函数和与其相关的引用环境组合的一个整体
    package main
    
    import "fmt"
    
    func add()func(int)int{
       var n int = 1
       return func(i int) int {
          n += i
          return n
       }
    }
    func main(){
       a := add()
       fmt.Println(a(1)) // 2
       fmt.Println(a(10)) //12
       fmt.Println(a(2)) //14
    }
    

      


    说明:
    1. add 是一个函数,返回的数据类型是一个匿名函数;
    2. 返回的匿名函数 引用到add 函数的变量n ,因此返回的匿名函数就和n 形成一个整体,构成闭包;
    3. 也可以这样理解: 闭包是类,函数是方法,n 是字段,函数和它使用的n 构成闭包;
    4. 因此当反复调用a 函数时, n 将累计;
    package main
    
    import (
       "fmt"
       "strings"
    )
    
    func addStr() func(str string) string {
       var base = "a"
       return func(str string) string {
          base += str
          return 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")) // ab
       fmt.Println(a("c")) // abc
       fmt.Println(a("d")) // abcd
       jpg := makeSuffix(".jpg")
       png := makeSuffix(".png")
       fmt.Println(jpg("a.jpg")) // a.jpg
       fmt.Println(jpg("b"))  //b.jpg
       fmt.Println(png("a.png"))  // a.png
       fmt.Println(png("b"))  //b.png 
    }
    

      


    1. 返回的匿名函数和参数suffix 变量组合成一个闭包,因此传入一次可以多次调用;
    ---
    ##### defer 函数
    defer 也称为延时执行;
    当函数执行到defer 时,暂时不执行,将defer 后的语句压入独立的栈,当函数执行完毕,return 前,再从defer 栈按照先入后出原则方式执行;
    package main
    
    import "fmt"
    
    func add(a,b int) int {
       defer fmt.Println("a=",a)  //10 输出顺序3 , 在将语句放入栈时,相关参数的值会进行拷贝
       defer fmt.Println("b=",b)  //20 输出顺序2 , 在将语句放入栈时,相关参数的值会进行拷贝
       a = 11
       sum := a + b
       defer fmt.Println("sum=",sum) //31  输出顺序1
       return sum
    }
    func main(){
       sum := add(10,20)
       fmt.Println(sum) //31 输出顺序4
    }
    

      


    ***在defer 将语句入到栈时,会将相关参数的值进行拷贝***
    1. 在Go 开发中通常会在创建资源(打开文件,连接数据库,锁资源)后使用defer file.Close() conn.Close()
    2. 在defer 后,仍可以继续使用已经创建的资源;
    ---
    ##### 函数参数传递方式
    上一节中已经讲过函数参数的值类型与引用类型,我们再来深入总结一下,这个知识点在编译型语言中很重要;
    1. 函数参数是值类型时就是值传递,函数参数是引用类型就是引用传递;
    2. 不管是值传递还是引用传递,传递给函数的都是变量的副本,不同的是值传递是值的拷贝,引用传递是地址的拷贝;
    3. 一般情况下地址拷贝效率高,而值拷贝由参数的数据大小, 数据越大,效率越低;
    4. 值类型: 基本数据类型int 系列, float 系列, bool, string, 数组,结构体;
    5. 引用类型: 指针,slice 切片, map , chan ,interface ;
    package main
    
    import "fmt"
    
    // 值传递
    func test01(a int){
       fmt.Printf("[test01] a value= %d a address=%p
    ",a,&a)
    }
    // 对于值传递希望更改原来的值可以传入变量地址
    func test03(a *int){
       *a = 100
       fmt.Printf("[test03] c value=%v c address=%p
    ",*a,&a)
    }
    // 引用传递
    func test02(a []int){
       fmt.Printf("[test02] b value=%v b address=%p
    ",a,&a)
    }
    func main(){
       var a int = 10
       fmt.Printf("a value= %d a address=%p
    ",a,&a)
       test01(a)
       var b = []int{1,2}
       fmt.Printf("b value=%v b address=%p
    ",b,&b)
       test02(b)
       var c = 10
       test03(&c)
       fmt.Printf("c value=%v c address=%p
    ",c,&c)
    }
    

      


    ---
    ##### 变量的作用域
    1. 函数内部声明或定义的变量是局部变量,作用域限于函数内部;
    2. 函数外部声明或定义的变量是全局变量,作用域在整个包有效,若首字母大写,则作用域为整个程序;
    3. 如果变量声明或定义在代码块内,如: if/for 代码块内,则作用域仅限于代码块;
    package main
    
    import "fmt"
    
    // 全局变量
    var name = "golang"
    // 如果首字母大写,则在整个程序中有效,其它包也可以使用
    var Age = 22
    
    func test01(){
       // 局部变量
       var a int = 10
       fmt.Println(a)
       // 使用全局变量
       fmt.Println(name)
       if a > 2 {
          // 代码块内部的局部变量, 仅限于if 代码块有效
          var d int = 100
          fmt.Println(d)
       }
       // 代码块内部的局部变量, 仅限于if 代码块有效
       //fmt.Println(d) // error
    }
    func main(){
       // 局部变量
       var b int = 1
       fmt.Println(b)
       // 使用全局变量
       fmt.Println(name)
       test01()
    }
    

      个人微信公众号上有最新文章: 欢迎大家关注一同学习交流

  • 相关阅读:
    C# CodeFirst(EF框架)代码优先创建数据库
    Entity Framework 配置关系(1对1,1对0)
    Entity Framework 配置关系(1对1,1对0)
    EFDbContext的使用
    EFDbContext的使用
    编程模式·观察者模式、事件通知、消息队列三者区别
    编程模式·观察者模式、事件通知、消息队列三者区别
    设计模式发布订阅方式实现异步并发
    设计模式发布订阅方式实现异步并发
    关于访问asp.net网站时登录后的奇怪问题
  • 原文地址:https://www.cnblogs.com/Mail-maomao/p/11411767.html
Copyright © 2020-2023  润新知