函数
每个函数声明都包含一个名字、一个形参列表、一个可选的返回列表以及函数体:
如果几个形参或者返回值的类型相同,那么类型只需要写一次:
函数类型称作为函数签名。当两个函数拥有相同的形参列表和返回列表时,认为这两个函数类型或签名是相同的。而形参和返回值的名字不会影响到函数类型。
形参变量都是函数的局部变量,初始值有调用者提供的实参传递。函数形参以及命名返回值同属于函数最外层作用域的局部变量
实参是按值传递的,所以函数接收到的是实参的副本;修改函数的形参变量并不会影响到调用者提供的实参。然而,如果提供的实参包含引用类型,比如指针、slice、map、函数或者通道,那么当函数使用形参变量时就有可能会间接地修改实参变量
偶尔会看到有些函数的声明没有函数体,那说明这个函数使用除了Go以外的语言实现。这样的声明定义了该函数的签名:
函数变量
函数变量就是把函数名赋值给一个变量:
函数类型的零值是nil(空值), 调用一个空的函数变量将报错:
函数变量可以和空值比较:
但函数本身不可比较,所以不可以互相进行比较或者作为键值出现在map中
匿名函数
在func关键字后面没有函数的名字,它就是一个表达式,它的值称为匿名函数
变长函数
在参数列表最后的类型名称之前使用省略号: “…”表示声明一个变长函数,调用这个函数时可以传递该类型任意数目的参数:
上面sum函数返回零个或者多个int参数。在函数体内,vals是一个int类型的slice
上面最后一个调用和下面的调用作用是一样的:
defer 延迟函数调用
语法上,一个defer语句就是一个普通的函数或方法调用,在调用之前加上关键字defer。函数和参数表达式会在语句执行时求值,但是无论是正常情况下,执行return语句或函数执行完毕,还是不正常的情况下,比如发生宕机,时间的调用推迟到包含defer语句的函数结束后才执行。defer语句没有限制使用次数;执行的时候以调用defer语句顺序的倒序进行。
defer语句经常适用于成对的操作,比如打开和关闭,连接和断开,加锁和解锁:
函数ReadFile包含了defer语句,所以当ReadFile执行完毕后,最后才执行f.close()
恢复
内建函数panic和recover
方法
方法的声明和普通函数的声明类似,只是在函数名字前面多了一个参数。这个参数把这个方法绑定到这个参数对应的类型上:
值接收者的方法
如果使用值接收者声明方法,调用时会使用这个值的一个副本来执行。
附加参数p称为方法的接收者
Go语言中,接收者不适用特殊名(比如this或者self);而是我们自己选择接收者名字,就像其他的参数变量一样。
同一个包下的任何类型都可以声明方法,只要它的类型既不是指针类型也不是接口类型
指针接收者的方法
由于主调函数会赋值每一个实参变量,如果函数需要更新一个变量,或者如果一个实参太大而我们希望避免复制整个实参,因为我们必须使用指针来传递变量的地址。这也同样适用于更新接收者:我们将它绑定到指针类型,比如:*user:
这个方法的名字是:(user).changeEmail。圆括号是必须的;没有圆括号,表达式会被解析为:(user.changeEmail)
本身是指针的类型是不能声明为指针接收者的方法:
方法变量
同函数变量
小结
- 值接收者使用值的副本来调用方法,而指针接收者使用实际值来调用方法