Golang📌基础📌N-函数.txt
golang函数特点:
	• 无需声明原型。
	• 支持不定变参。
	• 支持多返回值。
	• 支持命名返回参数。 
	• 支持匿名函数和闭包。
	• 函数也是一种类型,一个函数可以赋值给变量。
	• 不支持嵌套(nested),一个包不能有两个名字一样的函数。
	• 不支持重载(overload) 
	• 不支持默认参数(default parameter)。

函数声明包含一个函数名,参数列表,返回值列表和函数体。如果函数没有返回值,则返回列表可以省略。
函数可以没有参数或接受多个参数。函数可以返回任意数量的返回值。
使用关键字func定义函数,左大括号依旧不能另起一行。
函数是第一类对象,可作为参数传递。建议将复杂签名定义为函数类型,以便于阅读。
有返回值的函数,必须有明确的终止语句,否则会引发编译错误。

函数定义时有参数,该变量可称为函数的形参。形参就像定义在函数体内的局部变量。

在默认情况下,Go语言使用的是值传递,即在调用过程中不会影响到实际参数。
	注意1:无论是值传递,还是引用传递,传递给函数的都是变量的副本,不过,值传递是值的拷贝。
	引用传递是地址的拷贝,一般来说,地址拷贝更为高效。而值拷贝取决于拷贝的对象大小,对象越大,则性能越低。
	注意2:map、slice、chan、指针、interface默认以引用的方式传递。

不定参数传值就是函数的参数不是固定的,后面的类型是固定的。
Golang可变参数本质上就是slice。只能有一个,且必须是最后一个。
func f1(args ...int) {
} //0至多个参数
func f2(a string, args ...int) {
} //1至多个参数
func f3(a, b string, args ...int) {
} //2至多个参数
其中args是一个slice,我们可以通过arg[index]依次访问所有参数,通过len(arg)来判断传递参数的个数。

用any传递任意类型数据是Go语言的惯例用法,而且any是类型安全的。
func myfunc(args ...any) {
}

使用slice对象做变参时,必须展开。(slice...)

Go的返回值可以被命名,并且就像在函数体开头声明的变量那样使用。
返回值的名称应当具有一定的意义,可以作为文档使用。
没有参数的return语句返回各个返回变量的当前值。这种用法被称作“裸”返回。
func calc(a, b int) (sum int, avg int) {
	sum = a + b
	avg = (a + b) / 2
	return
}

多返回值可直接作为其他函数调用实参。
func test() (int, int) {
	return 1, 2
}
func add(x, y int) int {
	return x + y
}
func sum(n ...int) int {
	var x int
	for _, i := range n {
		x += i
	}
	return x
}
func main() {
	println(add(test()))
	println(sum(test()))
}

命名返回参数可被同名局部变量遮蔽,此时需要显式返回。
命名返回参数允许defer延迟调用通过闭包读取和修改。
func add(x, y int) (z int) {
	defer func() {
		z += 100
	}()
	z = x + y
	return
}
func main() {
	println(add(1, 2)) //103
}

显式return返回前,会先修改命名返回参数。
func add(x, y int) (z int) {
	defer func() {
		println(z) //203
	}()
	z = x + y
	return z + 200 // 执行顺序: (z = z + 200) -> (call defer) -> (return)
}
func main() {
	println(add(1, 2)) //203
}

在Go里面,函数可以像普通变量一样被传递或使用,Go语言支持随时在代码里定义匿名函数。
匿名函数由一个不带函数名的函数声明和函数体组成。匿名函数的优越性在于可以直接使用函数内的变量,不必声明。
	var f = func(x int) int {
		return x * x
	}
	println(f(5)) //25

所谓“闭包”,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。
func fib() func() int {
	i, j := 0, 1
	return func() int {
		i, j = j, i+j
		return i
	}
}
func main() {
	f := fib()
	println(f(), f(), f(), f(), f()) // 1 1 2 3 5
}

递归,就是在运行的过程中调用自己。构成递归需具备的条件:
	子问题须与原始问题为同样的事,且更为简单。
	不能无限制地调用本身,须有个出口,化简为非递归状况处理。
//正整数的阶乘递归实现
func factorial(i int) int {
	if i <= 1 {
		return 1
	}
	return i * factorial(i-1)
}

Golang延迟调用:
	1. 关键字defer用于注册延迟调用。
	2. 这些调用直到return前才被执。因此,可以用来做资源清理。
	3. 多个defer语句,按先进后出的方式执行。
	4. defer语句中的变量,在defer声明时就决定了。

通常在打开一个文件或执行一个网络请求后紧跟着defer一个关闭的资源、连接的方法。

延迟调用参数在注册时求值或复制,可用指针或闭包"延迟"读取。
	x, y := 1, 2
	defer func(i int) {
		println("df1:", i) //1
	}(x) //x修改前的值被copy
	defer func() {
		println("df2:", x) //3
	}() //x是修改后的值
	x = x + y
	println(x) //3

Golang没有结构化异常,使用panic抛出错误,recover捕获错误。

panic:
	假如函数F中书写了panic语句,会终止其后要执行的代码,在panic所在函数F内如果存在要执行的defer函数列表,按照defer的逆序执行。
	返回函数F的调用者G,在G中,调用函数F语句之后的代码不会执行,假如函数G中存在要执行的defer函数列表,按照defer的逆序执行。
	直到goroutine整个退出,并报告错误。

recover:
	用来控制一个goroutine的panicking行为,捕获panic,从而影响应用的行为,一般的调用建议。
	在defer函数中,通过recever来终止一个goroutine的panicking过程,从而恢复正常代码的执行。
	可以获取通过panic传递的error。

注意:
	利用recover处理panic指令,defer必须放在panic之前定义,另外recover只有在defer调用的函数中才有效。
	否则当panic时,recover无法捕获到panic,无法防止panic扩散。
	recover处理异常后,逻辑并不会恢复到panic那个点去,函数跑到defer之后的那个点。
	多个defer会形成defer栈,后定义的defer语句会被最先调用。

标准库errors.New和fmt.Errorf函数用于创建实现error接口的错误对象。通过判断错误对象实例来确定具体错误类型。

导致关键流程出现不可修复性错误的使用panic,其他使用error。