1. 前言

我们的go文档 开始章节,输出hello world!里,就有一个main() 函数。 对于函数我们并不陌生,函数是基本的代码块,是用于执行一个任务的,Go 语言实现功能程序最少都会有个 main() 函数。 下面就让我们来看看go函数的奥妙!

2. 函数定义

函数的定义格式:

func function_name( [parameter list] ) [return_types] {
   函数体
}

函数由 func 开始声明,声明包括 函数名,参数列表, 返回值列表和函数体。如函数无参数、无返回就可忽略, 函数的左大括号只能和函数名在同一行。

3. 函数参数与返回值

参数的类型是在变量后面,如果多个参数同类型,可只保留最后一个参数类型。 函数有返回值时,必须有终止语句,否则会编译报错。

package main

func main() {
    b := say("hello ", "world!")

    println(b)
}

// x,y参数类型相同可以保留最后一个
func say(x, y string) string {
    a := x + y

    return a
}

输出:

hello world!

3.1. 值传递和引用传递

值传递:将实际参数复制一份传递到函数中,在函数里对参数修改,不会影响到实际参数

引用传递:将实际参数的地址传递到函数中,在函数中对参数修改,影响到实际参数。

package main

import "fmt"

func main() {
    var x, y = 1, 2
    fmt.Printf("原始 x:%d,y:%d \n", x, y)

    value(x,y)
    fmt.Printf("值传值后 x:%d,y:%d \n", x, y)

    reference(&x, &y)

    fmt.Printf("引用传值后 x:%d,y:%d", x, y)
}

// 值传值
func value(x,y int) {
    x++
    y++
}

// 引用传值
func reference(x, y *int) {
    *x++
    *y++
}

输出:

原始 x:1,y:2
值传值后 x:1,y:2
引用传值后 x:2,y:3

3.2. 传递变长参数

函数最后一个参数是 ...type 的形式,这个函数就成为一个变长的参数,长度可以为 0,这样的函数称为变长函数。

go函数变长参数本质上就是切片。这个参数只能有一个,且必须是最后一个参数,在参数赋值时可以不用用一个一个的赋值,可直接传递一个数组或者切片。

func function_name(a, b, arg ...int) {}
package main

import (
    "fmt"
)

func main() {
    println(add("sum: %d", 1, 2, 3, 4))
}

func add(str string, n ...int) string {
    var a int
    for _, i := range n {
        a += i
    }

    return fmt.Sprintf(str, a)
}

输出:

sum: 10

4. 闭包

闭包是函数的嵌套,内层的函数可以使用外层函数的所有变量,即使外层函数已经执行完毕。

匿名函数可作闭包里内层函数使用,匿名函数是没有函数名的函数,可在一个函数体里定义匿名函数。 由一个不带函数名的函数声明和函数体组成。优点在于可直接使用函数内的变量,不用申明。

package main


func add() func() int {
    i := 1
    return func() int {
        i += 1
        return i
    }
}

func main() {
    // add1 为一个函数,i变量为1
    add1 := add()

    // 调用 add1 函数,i 变量自增 1 并返回
    println(add1())
    println(add1())
    println(add1())

    // 创建新的函数 add2,并查看结果
    add2 := add()
    println(add2())
    println(add2())
}

输出:

2
3
4
2
3

5. defer

关键字 defer 用于延迟调用,直到函数 return 前才被执,多个defer按照先进后出执行,另外defer语句中的变量,在defer声明时就决定了。 通常用于释放某些已分配的资源。

package main
import "fmt"

func main() {
    one()
}

func one() {
    fmt.Printf("one\n")
    defer two()
    fmt.Printf("three \n")
}

func two() {
    fmt.Printf("two")
}

输出:

one
three
two
  • 先进后出
package main

func main() {
    var arr [3]int
    for i := range arr {
        defer println(i)
    }
}

输出:

2
1
0
  • 在闭包上使用 defer
package main

func main() {
    var arr [3]int
    for i := range arr {
        defer func() { println(i) }()
    }
}

闭包用到的变量 i 在执行的时候已经变成2,所以输出全都是2

输出:

2
2
2

6. 递归函数

一个函数在其函数体内调用自身,就叫做递归函数。

  • 递归实现1+2+...+100
package main

func main() {

    a := add(100)

    println(a)
}

func add (a int) int {
    if a == 1 {
        return  1
    }

    return a + add(a -1)
}

输出:

5050

7. 异常处理

go 异常处理,可使用 panic 抛出错误,recover 捕获错误。常用步骤,go抛出panic异常,在defer中通过recover捕获这个异常,然后正常处理。

panic go内置函数,会终止其后要执行的代码,抛出异常(类似php中的throw)

func panic(v interface{})

recover go内置函数,可获取通过panic传递的error,获取异常(类似php中的catch),多次调用时,只有第一次能获取值

func recover() interface{}

使用defer(异常时函数结束返回)、panic(抛出异常)、recover(获取异常)进行异常处理:

package main

import "fmt"

func main() {
    add()
}

func add() {
    defer func() {
        if err := recover(); err != nil {
            fmt.Println(err.(string)) // 将 interface{} 转型为具体类型。
        }
    }()

    panic("panic error!")
}

输出:

panic error!
  • 实现类似 try catch 的异常处理
package main

import "fmt"

func main() {
    Try(func() {
        panic("test panic")
    }, func(err interface{}) {
        fmt.Println(err)
    })
}

func Try(fun func(), handler func(interface{})) {
    defer func() {
        if err := recover(); err != nil {
            handler(err)
        }
    }()
    fun()
}

输出:

panic error!
Copyright © yzx该文章修订时间: 2021-10-11 20:08:59

results matching ""

    No results matching ""