函数
# 函数
# 声明
func function_name( [parameter list] ) [return_types] {
函数体
}
2
3
可以定义以下类型函数
- 无惨无返回值函数
- 有一个参数的函数
- 有两个参数的函数
- 有一个返回值的函数
- 有多个返回值的函数
示例:
// 有参有返回值函数
func add(a int, b int) int {
c := a + b
return c
}
// 有参无返回值函数
func add(a int, b int) {
//xxxx
}
2
3
4
5
6
7
8
9
可变参数
func sum(numbers ...int) {
var all int
for index, number := range numbers {
index = index
all += number
}
fmt.Println("sum", all)
}
2
3
4
5
6
7
8
注意事项:
- 如果一个函数的参数是可变参数,同时还有其他的参数,那么可变参数要放在列表的最后。
- 一个函数的参数列表中最多只能有一个可变参数
# 参数传递
按照数据的存储特点区分:
- 值类型的数据:操作的是数据本身,如int、string、bool、float64、array
- 引用类型的数据:操作的是数据的地址,如slice、map、channel
# 值传递
func main() {
arr := [4]int{1, 2, 3, 4}
update(arr)
fmt.Println("update后的原数组:", arr)
}
func update(arr [4]int) {
fmt.Println("update接收到的数组:", arr)
arr = [4]int{0, 0, 0, 0}
fmt.Println("在update中修改后的数组:", arr)
}
// 输出
//update接收到的数组: [1 2 3 4]
//在update中修改后的数组: [0 0 0 0]
//update后的原数组: [1 2 3 4]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
以上对给定了大小的数组的参数传递是值传递(为指定大小是切片,就不是值传递了)
# 引用传递
这里使用切片,切片是不指定数组大小,如:arr2 := []int{1, 2, 3, 4}
,arr2就会是一个可扩容的数组,传递时是引用传递
package main
import "fmt"
func main() {
arr2 := []int{1, 2, 3, 4}
update2(arr2)
fmt.Println("update2后的原数组:", arr2)
}
func update2(arr []int) {
fmt.Println("update2接收到的数组:", arr)
arr[0] = 10086
arr = []int{0, 0, 0, 0}
fmt.Println("在update2中修改后的数组:", arr)
}
/**
输出:
update2接收到的数组: [1 2 3 4]
在update2中修改后的数组: [0 0 0 0]
update2后的原数组: [10086 2 3 4]
*/
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# defer
defer语义:推迟、延迟
在go语言中,使用德菲尔关键字可以延迟一个函数或者方法的执行。
package main
import "fmt"
func main() {
f("1")
fmt.Println("2")
defer f("3") //会被延迟到最后执行
fmt.Println("4")
}
func f(s string) {
fmt.Println(s)
}
/**
1
2
4
3
*/
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
- 你可以在函数中添加多个defer语句,当函数执行到最后时,这些defer语句会按照逆序执行,最后该函数返回,特别是当你在进行一些打开资源的操作时,遇到错误需要提前返回,在返回前你需要关闭相应的资源,不然很容易造成资源泄露等问题
- 如果有很多调用defer,那么defer是采用先进后出(栈)的模式,即最后一个defer的函数会先执行
# 返回值
函数可以有多个返回值,且返回值可以有名称或无名称
func returnFunc(a, b int) (sum int, err error) {
if a <= 0 || b <= 0 {
err = errors.New("参数不能小于1")
// 这里直接return就相当于下面一行的写法
//return
return 0, err
}
sum = a + b
return sum, nil
}
2
3
4
5
6
7
8
9
10
但是返回值中不能使用指针类型,否则会报错:panic: runtime error: invalid memory address or nil pointer dereference。意思是这个user因为是指针,不像int的sum这些是没有被初始化的,所以直接调用会报错。
所以加上一句
user = new(User)
先初始化user即可
type User struct {
username string
password string
}
func returnFunc(a, b int) (user *User, sum int, err error) {
// user = new(User)
user.username = "zdk"
user.password = "zdk"
if a <= 0 || b <= 0 {
err = errors.New("参数不能小于1")
// 这里直接return就相当于下面一行的写法
return
//return 0, err
}
sum = a + b
return user, sum, nil
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 匿名函数
package main
import "fmt"
func main() {
f1 := f
f1()
f2 := func() {
fmt.Println("匿名函数")
}
f2()
}
func f() {
fmt.Println("f函数")
}
/**
f函数
匿名函数
*/
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
在go中,函数本身也是一个变量,可以被赋值给另一个变量,同时变量可以定义为一个函数 即上面的f2变量的写法,感觉类似js
还可以使用以下写法来实现匿名函数并自己调用自己
func(a, b int) {
fmt.Println(a,b)
}(1,2)
//这段代码直接会被执行
//这是有返回值的匿名函数执行
res := func(a, b int) int {
return a + b
}(1, 2)
fmt.Println("res", res)
2
3
4
5
6
7
8
9
10
# 回调函数
高阶函数:go可以将一个函数作为另一个函数的参数,这个函数类型的参数就是回调函数,类比js,真的特别像
代码示例:
package main
import "fmt"
func main() {
res := operation(1, 2, add)
fmt.Println("res", res)
}
func operation(a, b int, fun func(a, b int) int) int {
return fun(a, b)
}
func add(a, b int) int {
return a + b
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 闭包
一个外层函数中,有内层函数,该内层函数中,会操作外层函数的局部变量并且该外层函数的返回值就是这个内层函数。 这个内层函数和外层函数的局部变量,统称为闭包结构 局部变量的生命周期就会发生改变,正常的局部变量会随着函数的调用而创建,随着函数的结束而销毁但是闭包结构中的外层函数的局部变量并不会随着外层函数的结束而销毁,因为内层函数还在继续使用
package main
import "fmt"
func main() {
r1 := increment()
v1 := r1()
fmt.Println(v1)
v2 := r1()
fmt.Println(v2)
fmt.Println(r1())
fmt.Println(r1())
fmt.Println(r1())
fmt.Println("------------------")
r2 := increment() //和r1指向同一个地址
v3 := r2()
fmt.Println(v3)
fmt.Println(r1())
fmt.Println(r2())
}
/** 输出:
1
2
3
4
5
------------------
1
6
2
*/
// 这里实际是返回一个 返回值为int的无参函数
func increment() func() int { //外层函数
//定义一个局部变量
i := 0
//定义一个匿名函数,给变量自增并返回
fun := func() int {
i++
return i
}
return fun
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
这里的局部变量i,在每个实例r1、r2中是独立的,它不会随着increment被重新赋给r3而重置,而是保持最后调用后的值