定义函数

关键字func

go
func sayHello(id int) {
fmt.Println("hello world", id)
}

多个参数类型一致可以省略

go
func sayHello(id, age int) {
fmt.Println("hello world", id)
}
go
func add(numberList ...int) {
var sum int
for _, number := range numberList {
sum += number
}
fmt.Println(sum)
}
  • numberList: 可变参数,类型为 []int(整数切片)
  • range: Go 语言的关键字,用于遍历数组、切片、映射等数据结构
  • _: 空白标识符,用于忽略索引值(range 返回索引和值两个值)
  • number: 接收遍历到的每个元素值的变量名

函数返回值

和TS类似,括号外面写返回值类型

go
func add2() int {
var sum int
for i := 1; i <= 100; i++ {
sum += i
}
return sum
}
func main() {
var res = add2()
fmt.Println(res)
}

返回多个值

返回值类型用括号括起来

go
func add3() (int, int) {
var sum int
for i := 1; i <= 100; i++ {
sum += i
}
return sum, 100
}
func main() {
var res2, res3 = add3()
fmt.Println(res2, res3)
}

命名返回值

相当于先定义再赋值,返回值的类型前加上变量名

go
func add4() (sum int, age int) {
for i := 1; i <= 100; i++ {
sum += i
}
age = 18
return
}
func main() {
var res4, res5 = add4()
fmt.Println(res4, res5)
}
5050 18

匿名函数

Go不能在函数体中定义函数,但是可以把函数赋值给变量,和js的箭头函数有点像

go
func main() {
var getName = func() string {
return "张三"
}
var name = getName()
fmt.Println(name)
}

高阶函数

把一个函数作为参数传到另一个函数里面,或者把函数作为参数传到其他的数据类型里面去

例子:

根据用户的不同输入采取不同的操作

传统写法

go
func get() {
fmt.Println("查询用户信息")
}
func addUser() {
fmt.Println("添加用户信息")
}
func del() {
fmt.Println("删除用户信息")
}
func main() {
var id int
fmt.Scan(&id)
switch id {
case 1:
get()
case 2:
addUser()
case 3:
del()
default:
fmt.Println("输入错误")
}
}

定义一个map,输入的id就是key,对应的value就是函数

go
var id int
fmt.Scan(&id)
var funMap = map[int]func(){
1: get,
2: addUser,
3: del,
}
fun, ok := funMap[id]
if ok {
fun()
}

闭包

设计一个函数,先传一个参数表示延时,后面再次传参数就是将参数求和

例如

go
fun(2)(1,2,3) // 延时2秒求1+2+3

写法一

go
package main
import (
"fmt"
"time"
)
func awaitAdd(awaitSecond int) func(...int) int {
time.Sleep(time.Duration(awaitSecond) * time.Second)
// 返回的是一个函数
return func(nunberList ...int) int {
var sum int
for _, number := range nunberList {
sum += number
}
return sum
}
}
func main() {
// 这里的timeRes是一个函数类型
timeRes := awaitAdd(2)
t1 := time.Now()
sumRes := timeRes(1, 2, 3)
sunTime := time.Since(t1)
fmt.Println(sumRes, sunTime) //6 0s
}

写法二

go
func awaitAdd(awaitSecond int) func(...int) int {
//time.Sleep(time.Duration(awaitSecond) * time.Second)
// 返回的是一个函数
return func(nunberList ...int) int {
time.Sleep(time.Duration(awaitSecond) * time.Second)
var sum int
for _, number := range nunberList {
sum += number
}
return sum
}
}
func main() {
// 这里的timeRes是一个函数类型
timeRes := awaitAdd(2)
t1 := time.Now()
sumRes := timeRes(1, 2, 3)
sunTime := time.Since(t1)
fmt.Println(sumRes, sunTime) //6 0s
}

这时候内层函数捕获外层变量: 闭包捕获了外层函数的参数 awaitSecond

  • 内层匿名函数“记住”了外层的 awaitSecond 变量
  • 即使外层函数执行完毕,awaitSecond 的值仍被保留在闭包中

可以函数嵌套函数

值传递和引用传递

go
func copyName(name string) {
fmt.Printf("%p\n", &name)
}
func main() {
name1 := "张三"
fmt.Printf("%p\n", &name1)
copyName(name1)
}
0xc000022070
0xc000022080

可以看到两个变量的内存地址并不一样,函数内部的操作并不会影响到原本的变量

如果要在函数内部修改变量需要传递内存地址

go
func SetName(name *string) {
fmt.Printf("%p\n", &name)
// 通过内存地址去找这个值
*name = "张三"
}
func main() {
name1 := "李四"
fmt.Printf("%p\n", &name1)
//copyName(name1)
SetName(&name1)
fmt.Println(name1)
}
0xc000022070
0xc00005c040
张三

指针

&是取地址,*是解引用,去这个地址指向的值

init函数

init()函数是一个特殊的函数,存在以下特性:

  1. 不能被其他函数调用,而是在main函数执行之前,自动被调用
  2. init函数不能作为参数传入
  3. 不能有传入参数和返回值

一个go文件可以有多个init函数,谁在前面谁就先执行

go
package main
import "fmt"
func init() {
fmt.Println("init1")
}
func init() {
fmt.Println("init2")
}
func init() {
fmt.Println("init3")
}
func main() {
fmt.Println("main")
}
init1
init2
init3
main

可以用来初始化全局变量

go
package main
import "fmt"
var db int
func init() {
db = 1
fmt.Println("init1")
}
func init() {
fmt.Println("init2")
}
func init() {
fmt.Println("init3")
}
func main() {
fmt.Println("main", db)
}
init1
init2
init3
main 1

初始化顺序

defer函数

  1. 关键字 defer 用于注册延迟调用
  2. 这些调用直到 return 前才被执。因此,可以用来做资源清理
  3. 多个defer语句,按先进后出的方式执行,谁离return近谁先执行
  4. defer语句中的变量,在defer声明时就决定了,即声明defer之前的变量
go
package main
import "fmt"
func main() {
defer fmt.Println("defer1")
defer fmt.Println("defer2")
defer fmt.Println("defer3")
return
}
defer3
defer2
defer1