go语言初识-函数及defer使用 | 青训营笔记

260 阅读3分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天

函数

为完成某一功能的程序指令(语句)的集合,称为函数。

在Go语言中,函数是第一类对象,我们可以将函数保持到变量中。函数主要有具名匿名之分,包级函数一般都是具名函数,具名函数是匿名函数的一种特例,当匿名函数引用了外部作用域中的变量时就成了闭包函数,闭包函数是函数式编程语言的核心。

格式

func fuction_name([parameter list])[return types]{
    函数体
}

具名函数

和c语言中的普通函数意义相同,具有函数名、返回值以及函数参数的函数。

func add(a, b int) int {
    return a + b
}

匿名函数

指不需要定义函数名的一种函数实现方式,它由一个不带函数名的函数声明和函数体组成。

var Add = func(a, b int) int {
    return a + b
}
fmt.Println(Add(1, 2))

名词解释

闭包函数:返回为函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域。

一级对象:支持闭包的多数语言都将函数作为第一级对象,就是说函数可以存储到变量中作为参数传递给其他函数,最重要的是能够被函数动态创建和返回。

:go的每一个文件都是属于一个包的,也就是说go是以包的形式来管理文件和项目目录结构的。

函数传参

Go语言中的函数可以有多个参数和多个返回值,参数和返回值都是以传值的方式和被调用者交换数据。在语法上,函数还支持可变数量的参数,可变数量的参数必须是最后出现的参数,可变数量的参数其实是一个切片类型的参数。

func main() {
    var a = []int{1, 2, 3}
    Print("name", "wht") 
    Print(a)             
}
​
func Print(a ...interface{}) {
    fmt.Println(a...)
}
//name wht
//[1 2 3]

函数返回值

不仅函数的参数可以有名字,也可以给函数的返回值命名。

func swap(a, b int) (c, d int) {
    c = b
    d = a
    return
}
func main() {
    a, b := 1, 2
    a, b = swap(a, b)
    fmt.Println(a, b)
}
//2 1
defer

defer一般用于资源的释放和异常的捕捉, 作为Go语言的特性之一.

  1. defer 语句会将其后面跟随的语句进行延迟处理. 意思就是说 跟在defer后面的语言 将会在程序进行最后的return之前再执行.

  2. 延迟函数的参数在defer语句出现时就已经确定了

  3. 在 defer 归属的函数即将返回时,将延迟处理的语句按 defer 的逆序进行执行,也就是说,先被 defer 的语句最后被执行,最后被 defer 的语句,最先被执行(defer进行压栈和弹栈过程)。

    func main() {
        a := 1
        defer fmt.Println(a)
        a = 2
        defer fmt.Println(a)
        a = 3
        defer fmt.Println(a)
    } 
    //3 2 1
    
  4. 延迟函数可能操作主函数的具名返回值

    当定义defer的函数(主函数)有返回值,返回值可能有名字(具名返回值),也可能没有名字(匿名返回值),延迟函数可能会影响返回值。

    func deferFuncReturn() (result int) {
        i := 1
        defer func() {
            result++
        }()
        return i
    }
    
    //实际会解析成以下操作
    result = i
    result ++
    return
    

    函数返回过程:关键字return不是一个原子操作,实际上return只代表汇编指令ret,即跳转程序执行。

    比如 return i ,实际上分两步执行,即先将 i 值存入栈中作为返回值,然后执行跳转,而defer的执行时机正是在跳转前,所以说defer执行时还是有机会操作返回值。