编辑
2022-09-23
Golang
00
请注意,本文编写于 790 天前,最后修改于 787 天前,其中某些信息可能已经过时。

目录

函数的声明
返回值
函数参数值的传递
作为值的函数(匿名函数)
闭包
可变参数
初始化函数(init)
延迟调用 (defer)

函数的声明

go
func function_name([params_list])([return_values_list]){ // 函数体 }
  • func关键字表示定义一个函数
  • function_name是函数名
  • params_list表示参数列表
  • return_values_list表示函数的返回值列表
  • 使用大括号包裹起来的部分称为函数体,是函数内部要执行的代码。

其中,参数列表和返回值列表是可选的。有些函数无需参数,有些参数运行后并不会有任何返回值,有些函数则无需参数也无需返回值

注意

go 语言不允许函数的嵌套,因此函数应各自独立定义

go
func fn_a(a string) string{ // do something return a } // 如果连续参数具有相同的类型,我们也可以进行速记声明 func fn_b(p1, p2 string) {} // 没有参数和返回值的函数 func fn_c(){ // do something } // 在别的函数调用一个函数 func main(){ rst := fn_a("123") fmt.Println(rst) } // 123

返回值

go
// go 中支持返回多个值 func fn_a()(string, int){ return "123", 5 } // go 还可以在函数体定义返回值的变量 // 不建议这么使用,会降低可读性 func fn_b()(s string, i int){ var s string = "123" var i int = 5 return }

注意

  1. 如果函数没有返回值,那么就是真的没有返回值,而不是默认返回一个值

image.png

函数参数值的传递

注意

直接传递一个变量名到另一个函数中,属于值传递 值传递不会修改原变量

go
func fn_a (num1 int, num2 int) { // 值传递 num1 = num1 + num2 } func main(){ var a int = 5 fn_a(a, 10) // 5 fmt.Println(a) }

如果想要修改原有的值,可以通过引用传递的方式实现(传递内存地址)

go
func main(){ var a int = 5 // 传递了内存地址 fn_a(&a, 10) // 15 fmt.Println(a) } func fn_a (num1 *int, num2 int) { // 引用传递,这里直接修改了 a 内存中存储的值 *num1 = *num1 + num2 }

还可以直接定义一个全局变量,这样可以直接操作修改

go
// 所有函数都能修改这个全局变量,数据存在一定的安全风险。 A int = 5 main (){ A += 10 }

作为值的函数(匿名函数)

go 中的函数可以直接作为一个值,类似 js 中的函数

go
func myFunction() { // 这里实际是创建了一个匿名函数,赋给了 fn fn := func() { fmt.Println("inside fn") } // 调用 fn() }

匿名函数可以直接运行,所以上面的代码可以进行简化

go
func myFunction() { // 这里实际是创建了一个匿名函数,直接调用,注意最后的括号 func() { fmt.Println("inside fn") }() }

闭包

闭包是指: 一个函数fn_a,是另一个函数fn_b的返回值

闭包意味着定义函数 fn_a 时访问 fn_b 范围内的值。

go
func fn_b() func(int) int { sum := 0 return func fn_a(v int) int { sum += v return sum } } add := fn_b() add(5) // 15 fmt.Println(add(10))

解析

可以根据结果看到,sum 变量并不在 fn_a 的作用域内,但是被绑定到 fn_a 上了

好处是,这里的 sum 其实并不在外部作用域中,而是在 fn_b 中。 所以每次调用 add,都能拿到上次修改后的 sum 值

使用闭包和使用普通函数的最大区别在于:

如果是普通函数,那就是一次性买卖,函数执行完毕后就无法再更改函数中变量的值

  • 使用闭包,函数就成为了一个变量的值。 只要变量还在,函数就会一直处于存活并独享内部状态。方便后期更改函数中变量的值。
  • 闭包还能起到一定的数据保护作用,比如这里的 sum 是不能再外部进行修改的

利用闭包的特性,可以进行工厂模式相关的设计

可变参数

函数中,可以使用...省略号运算符接受零个或多个参数的函数

go
func main() { sum := add(1,2,3,5) fmt.Println(sum) } // 注意这里的参数 func add(values ...int) int { sum := 0 for _, v := range values { sum += v } return sum }

初始化函数(init)

init是在main函数之前执行的特殊生命周期函数。 与 类似main,该init函数不接受任何参数,也不返回任何值

go
package main import "fmt" // 如果有多个 init 函数,则是按照定义顺序进行执行 func inti() { fmt.Println("初始化函数,先执行") } func main() { fmt.Println("main 函数,后执行") } /* $ go run main.go 初始化函数,先执行 main 函数,后执行 */

相关信息

init函数是可选的

适用于程序可能必不可少的任何全局设置,例如建立数据库连接、获取配置文件、设置环境变量等。

延迟调用 (defer)

defer 关键字,它可以让我们推迟一个函数的执行,直到周围的函数返回

go
func main() { defer fmt.Println("123") fmt.Println("234") }

image.png

如果有多个 defer,则会进入一个 defer stack(堆,先入后出) 即,先声明的后执行

go
func main() { defer fmt.Println("123") defer fmt.Println("234") fmt.Println("345") fmt.Println("456") }

image.png

相关信息

defer 语句以后进先出的方式堆叠和执行。

因此,Defer 非常有用,通常用于进行清理或错误处理。

本文作者:Silon汐冷

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!