Fork me on GitHub

Go语言之函数进阶

一、init函数

(一)简介

每一个源文件都可以包含一个init函数,该函数会在main函数执行前,被Go运行框架调用,也就是说init会在main函数前调用。

package main

import "fmt"

// init函数,通常可以在init函数中完成初始化工作
func init() {
    fmt.Println("init...")
}

func main() {
    fmt.Println("main...")
}
/*
输出:
init... main... */

(二)单包中变量、init、main中的调用

如果一个文件同时包含全局变量定义、init函数和main函数,则执行的流程是:全局变量定义--》init函数--》main函数

package main
import "fmt"

var age = test()

// 通过此函数可以看到是否调用变量
func test() int {
    fmt.Println("test...")
    return 12
}

func init() {
    fmt.Println("init...")
}

func main() {
    fmt.Println("main...")
}
/*
输出:

test...
init...
main...
*/

(三)多包中变量、init、main中的调用

多包组织:

  • utils.go
package utils

import "fmt"

var Age int

// Age 全局变量,在init函数中进行初始化
func init() {
    fmt.Println("utils包init...")
    Age = 20
}
  • main.go
package main

import (
    "fmt"
    "go_tutorial/day06/initFunc/03/utils" //引入utils包
)

var age = test()

func test() int {
    fmt.Println("test...")
    return 12
}

func init() {
    fmt.Println("init...")
    
}

func main() {
    fmt.Println("main...")
    fmt.Println("init...",utils.Age)

}

/* 输出
utils包init...
test...
init...
main...
init... 20
*/

如果main.go和utils.go中如果由变量定义、init函数,那么执行顺序是怎么样的呢?

 

main.go中先引入utils包,所以会执行utils.go中的变量定义、init函数。

 二、匿名函数

(一)局部匿名函数

Go支持匿名函数,匿名函数就是没有名字的函数。

  • 使用方式一

在定义匿名函数时就直接调用,这种方式匿名函数只能调用一次。

package main

import "fmt"

func main() {
    res := func(n1 int, n2 int) int {
        return n1 + n2
    }(5, 10)

    fmt.Println(res)

}
  • 使用方式二

将匿名函数赋值给一个变量,然后通过变量的方式进行调用。

package main

import "fmt"

func main() {

    a := func(n1 int, n2 int) int {
        return n1 + n2
    }

    res := a(10, 5)
    fmt.Println(res)
    
}

(二)全局匿名函数

如果将匿名函数赋值给一个全局变量,那么这个匿名函数就成为一个全局匿名函数。

package main

import "fmt"

var (
    Func = func(n1 int, n2 int) int {
        return n1 + n2
    }
)

func main() {
    // 全局匿名函数调用
    res := Func(10, 5)
    fmt.Println(res)

}

三、闭包 

闭包就是一个函数与其相关的引用环境组成的一个整体。

package main

import "fmt"

// 累加器
func AddUpper() func(int) int {

    var n int = 5
    return func(x int) int {
        n = n + x
        return n
    }

}

func main() {

    f := AddUpper()
    fmt.Println(f(1))
    fmt.Println(f(2))

}

/*
输出:
6
8
*/

AddUper是一个函数,它的返回值是一个匿名函数,这里匿名函数与变量n组成的就是一个闭包。闭包可以保存上次引用的n值,而不用反复传入。

四、defer

 在函数中,程序员经常需要创建资源(如:数据库连接、文件句柄等),为了在函数执行完毕后,及时释放资源,Go中提供defer延时机制。

package main

import "fmt"

func sum(n1 int, n2 int) int {
    // 当执行当defer会被延迟执行,先执行defer后面的语句
    // defer的语句会被压入到栈中,按照先如入后出的方式出栈
    // 当sum函数执行完毕后会执行defer
    defer fmt.Println("sum n1=", n1) // 第三步
    defer fmt.Println("sum n2=", n2) // 第二步

    res := n1 + n2
    fmt.Println("sum res=", res) // 第一步
    return res
}

func main() {

    res := sum(5, 10)
    fmt.Println("main res=", res) // 第四步

}
/*
输出:
sum res= 15
sum n2= 10
sum n1= 5
main res= 15
*/
  • 当go执行到一个defer时,不会立即执行defer后的语句,而是将defer后的语句到一个栈中,然后继续执行函数下一个语句
  • 当函数执行完毕后,再从defer栈中,一次从栈顶取出语句执行
  • 在defer将语句放入到栈中时,也会将相关的值拷贝同时入栈

defer主要的价值就是当函数执行完毕后可以及时释放函数创建的资源,如:

package main

import "fmt"


func test1() {
    // 关闭文件资源
    f := openfile("filePath")
    defer f.close()
    // 操作文件代码
    // ...
}

func test2() {
    // 关闭数据库资源
    connect := openDatabase("connect path")
    defer connect.close()
    // 操作数据库代码
    // ...

}

func main() {
    
}

 

posted @ 2021-11-21 10:24  iveBoy  阅读(48)  评论(0编辑  收藏  举报
TOP