函数的定义

1
2
3
4
5
6
7
8
func 函数名(参数)(返回值){
函数体
}

//函数名:由字母、数字、下划线组成。但函数名的第一个字母不能是数字。在同一个包内,函数名也称不能重名(包的概念详见后文)。
//参数:参数由参数变量和参数变量的类型组成,多个参数之间使用`,`分隔。
//返回值:返回值由返回值变量和其变量类型组成,也可以只写返回值的类型,多个返回值必须用`()`包裹,并用`,`分隔。
//函数体:实现指定功能的代码块
函数的参数和返回值都是可选的

函数的调用

通过函数名()的方式调用函数。

1
2
3
4
5
6
7
8
9
10
11
package main

import "fmt"

func sum(){
fmt.Println("Hello!")
}

func main(){
sum()
}
类型简写
1
2
func intSum(x, y int)
//intSum函数有两个参数,这两个参数的类型均为int,因此可以省略x的类型,因为y后面有类型说明,x参数也是该类型。

可变【不定长】参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main

import "fmt"

func intSum2(x ...int) int {
fmt.Println(x) //x是一个切片
sum := 0
for _, v := range x {
sum = sum + v
}
return sum
}

func main(){
ret1 := intSum2()
ret2 := intSum2(10)
ret3 := intSum2(10, 20)
ret4 := intSum2(10, 20, 30)
fmt.Println(ret1, ret2, ret3, ret4)
}
定长参数搭配可变参数使用时,可变参数要写在最后面
1
func intSum3(x int, y ...int) int {

返回值

Go通过关键字return向外输出返回值

1
2
3
4
5
func calc(x, y int) (int, int) {
sum := x + y
sub := x - y
return sum, sub
}
返回值命名
1
2
3
4
5
func calc(x, y int) (sum, sub int) { //sum和sub在定义时命名,return时可以不写名字
sum = x + y
sub = x - y
return
}
返回值补充

当我们的一个函数返回值类型为slice时,nil可以看做是一个有效的slice,没必要显示返回一个长度为0的切片。

1
2
3
4
5
func someFunc(x string) []int {
if x == "" {
return nil // 没必要返回[]int{}
}
}

全局变量和局部变量

全局变量是定义在函数外的变量,作用域是整个代码块。局部变量是定义在函数内的变量,作用域是单个函数。如果局部变量和全局变量重名,优先访问局部变量。for循环语句中定义的变量,也是只在for语句块中生效:

自定义函数类型

使用type关键字来定义一个函数类型:

1
2
3
4
5
6
7
8
9
10
type calculation func(int, int) int
//此处我们定义了一个calculation类型的函数,它有两个int型的形参和一个int型的返回值

//add函数就属于我们定义的calculation类型函数,它可以直接赋值给calculation函数类型的变量
func add(x, y int) int {
return x + y
}
//赋值给calculation函数类型
var c calculation
c = add

函数作为参数

1
2
3
4
5
6
7
8
9
10
func add(x, y int) int {
return x + y
}
func calc(x, y int, op func(int, int) int) int {//op是函数名
return op(x, y)
}
func main() {
ret2 := calc(10, 20, add)
fmt.Println(ret2) //30
}
匿名函数

Go语言中函数内部不能再像之前那样定义函数,定义格式:

1
2
3
func(参数)(返回值){
函数体
}

没有函数名,就不能通过函数名调用,匿名函数需要保存到某个变量或者作为立即执行函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
func main() {
// 将匿名函数保存到变量
add := func(x, y int) {
fmt.Println(x + y)
}
add(10, 20) // 通过变量调用匿名函数

//自执行函数:匿名函数定义完加()直接执行
func(x, y int) {
fmt.Println(x + y)
}(10, 20)
}
//匿名函数多用于实现必闭包和回调函数
回调函数

在编写应用程序时,库函数负责提供某些封装好的功能,我们会会经常调用这些库函数,而有的库函数在被调用时需要应用程序先传递给它一个函数,在以便在合适时调用,这个被传入后又被调用的函数就称为回调函数;而把回调函数传入库的动作被称为回调函数登记。

回调函数的三方

中间函数【接受回调函数的函数】,回调函数,起始函数【即主函数】
阻塞式回调:回调函数的调用发生在起始函数返回值之前;
延迟式回调:回调函数的调用发生在起始函数返回值之后。

1
2
3
4
5
6
7
8
9
10
func a() func() { //函数a的返回值也是一个函数
return func() {
fmt.Println("this is ", name)
}
}

func main() {
res := a() //将函数a的返回值赋值给变量res
res() //调用res就相当于调用函数a中的匿名函数
}

闭包

闭包组成要素:
#函数 + #外层变量的引用

1
2
3
4
5
6
7
8
9
10
11
func a() func() { //函数a的返回值也是一个函数
name := "Daqian"
return func() {
fmt.Println("this is ", name)
}
}

func main() {
res := a() //此时的res就是一个简单的闭包,这里将a中的匿名函数和外部变量name一起返回给res
res() //调用res就相当于调用函数a中的匿名函数和匿名函数的外部变量name
}

定义进化:

1
2
3
4
5
6
7
8
9
10
func a(name string) func() { //将name改成函数参数,由调用者传入值
return func() {
fmt.Println("this is ", name)
}
}

func main() {
res := a("Xi Gua") //将函数a的返回值赋值给变量res
res() //调用res就相当于调用函数a中的匿名函数
}

定义进化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//makeSuffixFunc函数需要传入一个string,返回一个函数,这个函数接收一个string参数并且返回一个string参数。
func makeSuffixFunc(suffix string) func(string) string {
return func(name string) string {
if !strings.HasSuffix(name, suffix) {//这里判断字符串的后缀,如果不是makeSuffixFunc函数参数所期望的后缀就返回一个返回值函数的参数+makeSuffixFunc函数参数的结果,这个结果是一个string。
return name + suffix
}
return name
}
}

func main() {
jpgFunc := makeSuffixFunc(".jpg")
txtFunc := makeSuffixFunc(".txt")
fmt.Println(jpgFunc("test")) //test.jpg
fmt.Println(txtFunc("test")) //test.txt
}

延迟处理语句

defer:会将跟在其后面的语句进行延迟处理。多个defer语句会按逆顺序执行,即后被defer的语句先执行

1
2
3
4
5
6
7
8
9
10
package main

import "fmt"

func main() {
x := 1
defer fmt.Println(x)
x = 10
defer fmt.Println(x)
}
defer在return中的执行顺序
1
2
3
4
5
6
7
8
9
10
11
12
13
//return语句底层执行顺序
return x
1 - 返回值 = x
2 - return 返回值

//defer语句在return中的执行顺序
func f2()(x int){
defer func{
x++
}()
return 5
}
1 -

常用函数

close:主要用于关闭channel
len:用来求长度
new:用来分配内存,针对值类型,比如int、struct,返回值是指针
make:用来分配内存,针对引用类型,比如chan、map、slice
append:追加元素到数组、slice中
panic和recover:错误处理

panic / recover:

panic的作用是引发异常,从而使程序停止执行。它可以在任何地方引发异常。
recover的作用是恢复panic引发的异常,使程序继续正常执行。recover只能在defer语句中使用,而defer语句则在可能引发panic之前定义。

闭包的特性:

  • 闭包通常以函数的形式定义,它的返回值是一个匿名函数
  • 内存逃逸;可以通过闭包的返回值访问并操作闭包内匿名函数引用的外部局部变量
1
2
3
4
5
6
7
8
func add() func() int {
    sum := 0 //外部变量
    return func() int {
        sum += 1
        return sum
    }

}