函数
函数声明
- 函数声明包含了func关键字,一个函数名称,一个形参列表、一个可选的返回列表以及函数体
func name(parameter-list)(result-list){
body
}
- 函数的类型称作函数签名。当两个函数也有相同的参数列表和返回列表,认为这两个函数的类型或签名是相同的。而形参和返回值的名字不会影响到函数类型
- 形参变量都是函数的局部变量,初始值由调用者提供的实参传递。函数形参和命名返回值同属于函数最外层作用域的局部变量。
- 实参是按值传递的,所以函数接收到的每个实参的副本。
- 当实参是引用类型时,包括指针、slice、map、函数、通道,当函数使用形参变量时就可能会间接地修改实参变量。
- 有些函数只有声明没有函数体,说明这个函数使用了除Go以外的语言实现。
递归
多返回值
- 一个函数如果有命名的返回值,则可以省略return语句的操作数,称为裸返回
func functionName(x,y int)(m,n int) {
m,n = x,y
return //裸返回
}
//返回值没有命名 未命名返回值
func functionName2(x,y int)(int,int) {
return y,x
}
错误
- 当函数调用发生错误时返回一个附加的结果作为错误值,习惯上将错误值作为最后一个结果返回。
如果错误只有一种情况,结果通常设置为布尔类型。
v,ok := cache.Lookup(key)
if !ok{
//cache[key]不存在。。。
}
- 更多时候,尤其对于IO操作。错误的原因多种多样,而调用者需要一些详细的信息,在这种情况下,错误的结果类型往往是error。
- 当一个函数调用返回一个错误时,调用者应该负责检查错误并采取合适的处理应对。比如将错误传递下去。将错误封装构建新的错误消息。
fmt.Errorf("","")
fmt.Sprintf("","")
log.Fatalf("ssssss%v\n",err) //打印出日志后退出程序
func functionName2(x,y int)(int,int,error) {
err := fmt.Errorf("", "") //fmt.Errorf("","")包装错误消息 返回一个错误
fmt.Sprintf("","")
err = io.EOF
return y,x,err
}
- 对于不固定或不可预测的错误,在短暂的间隔后对操作进行重试,超出一段的重试次数或限定时间后再报错推出。(指数退避策略)
函数变量
- 函数在Go语言中是头等重要的值。函数变量也有类型,而且可以赋值给变量或者传递或者从其他函数中返回。函数变量可以像其他函数一样调用。
- 函数类型的零值是nil(空值),调用一个空的函数变量将导致宕机。函数变量可以和空值相比较。函数变量之间不可以互相比较或者作为键出现在map中。
匿名函数
- 命名函数只能在包级别的作用域进行声明。但使用函数字面量可以在任何表达式内指定函数变量。函数字面量就像函数声明,但在func关键字后面没有函数的名称。他是一个表达式,它的值叫做匿名函数。
func main() {
s := strings.Map(func(r rune) rune {
return r + 1
}, "abc")
fmt.Println(s) //bcd
}
闭包
- 一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包(closure)。也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域。在 JavaScript 中,每当创建一个函数,闭包就会在函数创建的同时被创建出来。Go语言中以这种方式定义的函数,里层的函数可以获取和更新外层函数定义的局部变量。这些隐藏的变量引用就是我们把函数归类为引用类型而且函数变量无法进行比较的原因。
//变量i在main函数中返回squares()函数后依旧存在
func squares() func() int {
var i int
return func() int {
i++
return i*i
}
}
func main() {
f:= squares()
fmt.Println(f()) //1
fmt.Println(f()) //4
fmt.Println(f()) //9
fmt.Println(f()) //16
fmt.Println(f()) //25
fmt.Println(squares()()) //1
fmt.Println(squares()()) //1
fmt.Println(squares()()) //1
fmt.Println(squares()()) //1
fmt.Println(squares()()) //1
//如果是直接调用squares()函数,不会存在闭包。函数squares赋给f,函数变量f中包含了对变量i的引用
}
捕获迭代变量
- 每次循环产生的函数,其记录的是循环变量的地址,所有循环产生的函数公用这一个值。
变长函数
fmt.Println("变长函数:",varPara(1,2,3,4))
param := []int{1,2,3,4}
fmt.Println("slice变长函数求sum:",varPara(param...))
func varPara( pa ...int) int {
var sum int
for _, i2 := range pa {
sum+= i2
}
return sum
}
延迟函数调用defer
- 在包含defer语句的函数结束后执行
- defer执行的时候以defer语句顺序的倒序进行。LIFO
func sum(x, y int) int {
fmt.Println(x)
fmt.Println(y)
defer fmt.Println("x:",x)
defer fmt.Println("y:",y) //defer倒序执行
return x+y
}