有任何问题或建议,请及时交流碰撞。我的公众号是【脑补成炸鱼】,GitHub地址:https://github.com/eddycjy。大家好,我是炸鱼。在Go语言中,说到函数,最常提到的就是“Go语言函数是一等公民”。这个定义来的很突然。先来了解一下什么是一等公民。根据维基百科对一等公民的定义:在编程语言设计中,给定编程语言中的一等公民(也称为类型、对象、实体或值)是支持其他实体通常可用的所有操作的实体。这些操作通常包括作为参数传递、从函数返回、修改和分配给变量。在编程语言设计中,给定编程语言中的一等公民(即类型、对象、实体或值)可以将函数赋值给变量,也可以直接将函数用作其他函数的参数或返回值。Go语言中的函数也满足这个定义,因此常被称为“一等公民”。了解清楚背景后,下一步就是进一步扩展了。Go语言中普通函数的定义格式为func[函数名](入参)(出参),如下:funccallFuncA(x,ystring)(sstring,errerror){returnx+y,nil}funcmain(){callFuncA("fried","friedfish")}示例代码中声明了一个方法callFuncA,只允许在包内调用,所以首字母小写。它有两个输入参数,x和y,都是字符串类型。输出参数是变量s和err,类型分别是string和error。另外,在函数体中返回值时,也可以使用快捷方式返回:funccallFuncA(x,ystring)(sstring,errerror){s=x+yreturn}输出时声明的变量名参数是可以应用于它自己的功能。因此,如果直接执行return,会隐式返回声明的输出变量。定义函数时,其输入参数也支持可变参数的语法:funccallFuncA(x...string)(sstring,errerror){s=strings.Join(x,",")return}funcmain(){fmt.Println(callFuncA("fried","friedfish"))}在入参变量上声明为x...string,表示变量x为string类型的可变变量,可以在输入参数中传入多个字符串参数。变量variable传入的格式是slice类型,我们会在后面的章节进行讲解。可以理解为一个没有长度限制的动态数组:[0:Fried1:Friedfish]可变变量常见的后续操作多为循环遍历处理,或者拼接等操作。匿名函数Go语言默认也支持匿名函数的声明。声明方式和普通函数几乎一样:funcmain(){s:=func(x,ystring)(sstring,errerror){returnx+y,nil}s("fried","friedfish")}匿名函数可以在任何地方声明,不需要定义函数名,如果在函数体后面紧跟(),表示声明后立即执行:funcmain(){s,_:=func(x,ystring)(sstring,errerror){returnx+y,nil}("fried","friedfish")}在所有函数类的使用中,一件事是很重要,就是对函数变量作用域的理解:funcmain(){x,y:="fried","friedfish"s,_:=func()(sstring,errerror){returnx+y,nil}()fmt.Println(s)}这个匿名函数没有形参,函数内部也没有定义相应的变量。这时,它读取全局x和y变量的值,输出结果为“炸鱼”。funcmain(){x,y:="fried","friedfish"_,_=func(x,ystring)(sstring,errerror){x="eat"returnx+y,nil}(x,y)fmt.Println(x,y)}这个匿名函数有形参,但是变量x在函数内部被重新赋值。那么最终对外输出的变量x的值是多少呢?输出是“炸鱼”。为什么在函数中对变量x重新赋值后,全局变量x的值还是没有改变?本质原因是作用域不同,函数内部修饰的变量x是函数内部的局部变量。外部的是全局变量,属于不同的作用域。以结合结构体(struct)的方式,结构体方法可以声明属于该结构体的方法:typeTstruct{}funcNewT()*T{return&T{}}func(t*T)callFuncA(x,ystring)(sstring,errerror){returnx+y,nil}funcmain(){NewT().callFuncA("fried","friedfish")}具体函数使用方法同普通功能,没有其他区别。结构体相关的值传递和引用传递方法调用将在后续章节展开。内置函数Go语言本身支持一些内置函数,这些内置函数的调用不需要引用第三方标准库。内置函数的作用是配合Go语言的一般使用,数量很少。如下:用于获取某些类型的长度和容量:len、cap。用于创建和分配某些类型的内存:new、make。对于错误处理机制(异常panic、异常捕获):panic、recover。对于复制和添加切片:复制、追加。对于简单的输出信息:print、println。用于处理复数:complex、real、imag。对于每一个内置函数的实际使用场景,我们会在后续章节进一步展开,因为每一个内置函数本质上都对应着各种类型的使用场景。小结本章我们介绍了为什么函数在Go语言中被称为一等公民,并对函数的各种变换进行了基本描述:普通函数、匿名函数、结构方法、内置函数。面对初学者最容易出错的函数作用域问题,也进行了一个基础的整理。这条忠告大家要深入思考,多理解,以免以后踩坑。我的公众号分享Go语言、微服务架构和奇怪的系统设计。欢迎关注我的公众号与我交流交流。最好的关系是相互成就。您的喜欢是创作炸鱼最大的动力。感谢您的支持。
