> For the complete documentation index, see [llms.txt](https://yangsx95.gitbook.io/notes/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://yangsx95.gitbook.io/notes/programming-language/golang/han-shu.md).

# 函数

> Go语言的函数，很像JavaScript语言的函数

```go
func 函数名称 (形参1, 形参2) (返回值类型列表) {
  函数体
  retrun 返回值列表
}
```

## 函数的声明

### 函数的命名

* 函数名也是标识符，遵循标识符的命名规范
* 首字母小写，只能被本包使用
* 首字母大写，可以被本包以及其他包使用

### 重载：不支持

```go
// 以下代码会报错
// 编译器会认为下面的两个方法定义是重复的
func test() {}

func test(a string) {}
```

### 无参函数

```go
func test() {}
```

### 有参函数：参数类型一致

```go
func test(x, y float32) {}
```

### 有参函数：类型不一致

```go
func test(x string, y int) {}
```

### 有参函数：可变参数

```go
func test(names...int) {
  // 可变参数在方法内会被转换为切片，可以对切片进行遍历取值
}
```

### 无返回值函数

如果是无返回函数，返回值类型不填

```go
func test() {
}
// 调用函数
test()
```

### 单返回值函数

如果只有一个返回值，可以省略括号

```go
func test() string {
  return "hello"
}
// 调用函数
var s = test()
```

### 多返回值函数

函数可以有多个返回值，多个返回值返回使用逗号隔开

```go
func test() (string, int) {
  return "hello", 20
}
// 调用函数
var s, i = test()
```

### 带有变量名的返回值

```go
func namedRetValues() (a, b int) {
    a = 1
    b = 2
    return
}
// 直接返回a 和 b
```

## 函数的调用

### 调用多返回值函数

```go
var s, i = test()
```

### 忽略多返回值函数返回值

```go
// 调用函数，忽略部分返回值
var s, _ = test()
```

## 函数参数的值传递与引用传递

### 基本类型形参是值传递

```go
// 交换两个数字
func exchangeNum(num1 int, num2 int) {
	var t int
	t = num1
	num1 = num2
	num2 = t
}

func main() {
	var i = 10
	var j = 20
	exchangeNum(i, j)
	fmt.Printf("i = %d, j = %d", i, j)  // 结果为 i = 10, j = 20
}
```

使用指针可以做到引用传递：

```go
// 交换两个数字
// 参数类型为指针
func exchangeNum(num1Ptr *int, num2Ptr *int) {
	var t int
	t = *num1Ptr
	*num1Ptr = *num2Ptr
	*num2Ptr = t
}

func main() {
	var i = 10
	var j = 20
	exchangeNum(&i, &j)
	fmt.Printf("i = %d, j = %d", i, j) // 结果为 i = 20, j = 10
}
```

### 引用类型形参是引用传递

* 指针
* 切片
* map
* 函数
* channel

等数据类型。

## 匿名函数

### 定义并直接调用匿名函数

```go
func(data int) {
    fmt.Println("hello", data)
}(100)
```

### 变量函数

```go
// 将匿名函数体保存到f()中
f := func(data int) {
    fmt.Println("hello", data)
}
// 使用f()调用
f(100)
```

### 变量函数传递

```go
package main
import (
    "fmt"
)
// 遍历切片的每个元素, 通过给定函数进行元素访问
func visit(list []int, f func(int)) {
    for _, v := range list {
        f(v)
    }
}
func main() {
    // 使用匿名函数打印切片内容
    visit([]int{1, 2, 3, 4}, func(v int) {
        fmt.Println(v)
    })
}
```

### 匿名函数应用技巧

```go
func main() {
	var skill = map[string]func(){
		"fire": func() {
			fmt.Println("chicken fire")
		},
		"run": func() {
			fmt.Println("soldier run")
		},
		"fly": func() {
			fmt.Println("angel fly")
		},
	}
	skill["fire"]()
}
```

## 闭包（Closure）

闭包就是能够读取其他函数内部变量的函数，在其他语言中，也称为Lambda表达式。在Golang中，闭包组成如下：

```go
匿名函数 + 引用环境 = 闭包
```

下面是一个闭包累加器：

```go
package main

import "fmt"

// 定义一个名为getSum的函数，无参，返回值是一个func (int) int 的函数
func getSum() func (int) int {
	var sum = 0
	return func(num int) int { // 这个拥有sum变量的匿名函数就是闭包
		sum = sum + num // sum变量的作用域被扩大，将会被一直保存在内存中，所以要适当使用闭包
		fmt.Println(sum)
		return sum
	}
}

func main() {
	sumF := getSum()
	sumF(1) // 1. 入门
	sumF(2) // 3
	sumF(3) // 6
}
```

结果：

```shell
$ go run main.go 
1
3
6
```

## defer 延迟执行语句

类似于Java的Finally语句，通常用于标记关闭资源的语句，让其使用完毕后自动关闭：

```go
package main
import (
	"fmt"
)
func main() {
	testDefer()
}
func printReturn() int {
	fmt.Println("defer return")
	return 0
}

func testDefer() int {
	fmt.Println("defer begin")
	var num1 int = 30
	var num2 int = 60

	// 将defer放入延迟调用栈，这是一个新的栈空间
	// 涉及的变量如果是基本类型，则会进行值传递，如果是引用类型则是传递引用
  // 因为num1和num2是基本类型，所以打印30和60
	defer fmt.Println(num1) // 先放入的是栈底，最后调用
	defer fmt.Println(num2) // 后放入的是栈顶，最先调用

	num1 += 10
	num2 += 10

	fmt.Println("num1 和 num2:", num1, num2)

	return printReturn() // defer 在return语句之后运行
}
```

执行结果如下：

```shell
$ go run main.go 
defer begin
num1 和 num2: 40 70
defer return
60
30
```
