目录
函数
为完成某一功能的程序指令(语句)的集合,称为函数。
在Go语言中,函数是第一类对象,我们可以将函数保持到变量中。函数主要有具名和匿名之分,包级函数一般都是具名函数,具名函数是匿名函数的一种特例,当匿名函数引用了外部作用域中的变量时就成了闭包函数,闭包函数是函数式编程语言的核心。
格式
func fuction_name([parameter list])[return types]{
函数体
}
具名函数
和c语言中的普通函数意义相同,具有函数名、返回值以及函数参数的函数。
func add(a, b int) int {
return a + b
}
匿名函数
指不需要定义函数名的一种函数实现方式,它由一个不带函数名的函数声明和函数体组成。
var Add = func(a, b int) int {
return a + b
}
fmt.Println(Add(1, 2))
名词解释
闭包函数:返回为函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域。
一级对象:支持闭包的多数语言都将函数作为第一级对象,就是说函数可以存储到变量中作为参数传递给其他函数,最重要的是能够被函数动态创建和返回。
包:go的每一个文件都是属于一个包的,也就是说go是以包的形式来管理文件和项目目录结构的。
函数传参
Go语言中的函数可以有多个参数和多个返回值,参数和返回值都是以传值的方式和被调用者交换数据。在语法上,函数还支持可变数量的参数,可变数量的参数必须是最后出现的参数,可变数量的参数其实是一个切片类型的参数。
func main() {
var a = []int{1, 2, 3}
Print("name", "wht")
Print(a)
}
func Print(a ...interface{}) {
fmt.Println(a...)
}
//name wht
//[1 2 3]
函数返回值
不仅函数的参数可以有名字,也可以给函数的返回值命名。
func swap(a, b int) (c, d int) {
c = b
d = a
return
}
func main() {
a, b := 1, 2
a, b = swap(a, b)
fmt.Println(a, b)
}
//2 1
defer
defer一般用于资源的释放和异常的捕捉, 作为Go语言的特性之一.
-
defer 语句会将其后面跟随的语句进行延迟处理. 意思就是说 跟在defer后面的语言 将会在程序进行最后的return之前再执行.
-
延迟函数的参数在defer语句出现时就已经确定了
-
在 defer 归属的函数即将返回时,将延迟处理的语句按 defer 的逆序进行执行,也就是说,先被 defer 的语句最后被执行,最后被 defer 的语句,最先被执行(defer进行压栈和弹栈过程)。
func main() { a := 1 defer fmt.Println(a) a = 2 defer fmt.Println(a) a = 3 defer fmt.Println(a) } //3 2 1
-
延迟函数可能操作主函数的具名返回值
当定义defer的函数(主函数)有返回值,返回值可能有名字(具名返回值),也可能没有名字(匿名返回值),延迟函数可能会影响返回值。
func deferFuncReturn() (result int) { i := 1 defer func() { result++ }() return i }
//实际会解析成以下操作 result = i result ++ return
函数返回过程:关键字return不是一个原子操作,实际上return只代表汇编指令ret,即跳转程序执行。
比如 return i ,实际上分两步执行,即先将 i 值存入栈中作为返回值,然后执行跳转,而defer的执行时机正是在跳转前,所以说defer执行时还是有机会操作返回值。
方法
方法一般是面向对象编程(OOP)的一个特性,在C++语言中方法对应一个类对象的成员函数,是关联到具体对象上的虚表中的。但是Go语言的方法却是关联到类型的,这样可以在编译阶段完成方法的静态绑定。一个面向对象的程序会用方法来表达其属性对应的操作,这样使用这个对象的用户就不需要直接去操作对象,而是借助方法来做这些事情。
c语言中对文件描述符操作可能会定义以下函数
// 文件对象
type File struct {
fd int
}
// 打开文件
func OpenFile(name string) (f *File, err error) {
// ...
}
// 关闭文件
func CloseFile(f *File) error {
// ...
}
// 读文件数据
func ReadFile(f *File, offset int64, data []byte) int {
// ...
}
但函数都是普通函数,并不是面向对象的思想
go语言使用,在函数名前传入类型指针,即C++中this指针概念
func OpenFile(name string) (f *File, err error) {
// ...
}
func (f *File) CloseFile() error {
// ...
}
func (f *File) ReadFile(offset int64, data []byte) int {
// ...
}
从代码角度看虽然只是一个小的改动,但是从编程哲学角度来看,Go语言已经是进入面向对象语言的行列了。我们可以给任何自定义类型添加一个或多个方法。每种类型对应的方法必须和类型的定义在同一个包中,因此是无法给int这类内置类型添加方法的(因为方法的定义和类型的定义不在一个包中)。对于给定的类型,每个方法的名字必须是唯一的,同时方法和函数一样也不支持重载。