🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] # 导包 1. 点操作   有时候会看到如下的方式导入包     import( . “fmt” )  这个点操作的含义就是这个包导入之后在你调用这个包的函数时,你可以省略前缀的包名,也就是前面你调用的fmt.Println(“hello world”)  可以省略的写成Println(“hello world”)       2. 别名操作   别名操作顾名思义可以把包命名成另一个用起来容易记忆的名字            import( f “fmt” )   别名操作调用包函数时前缀变成了重命名的前缀,即f.Println(“hello world”)       3.  _操作   这个操作经常是让很多人费解的一个操作符,请看下面这个import           import ( “database/sql” _ “github.com/ziutek/mymysql/godrv” )            _操作其实只是引入该包。当导入一个包时,它所有的init()函数就会被执行,但有些时候并非真的需要使用这些包,仅仅是希望它的init()函数被执 行而已。这个时候就可以使用_操作引用该包了。即使用_操作引用包是无法通过包名来调用包中的导出函数,而是只是为了简单的调用其init函数() # 函数定义 ~~~ func 函数名(/*参数列表*/) (ol type1, ol2 type2 /*返回类型*/) { //函数体 return v1, v2 //返回多个值 } ~~~ 函数名小写为private,大写为public 参数列表不支持默认参数 返回类型,可以只有类型没有变量名 # 不定参数类型 参数个数为不定数量 ~~~ ...type只能作为函数的参数类型存在,并且是最后一个参数 ~~~ ~~~ func 函数名(args ...int) { } ~~~ ~~~ func MyFunc(args ...int) { //获取用户传递参数的个数 fmt.Println("len(args) = ", len(args)) for i := 0; i<len(args); i++ { fmt.Printf("args[%d] = %d\n", i, args[i]) } fmt.Println("==========================") //返回2个值一个是下标,一个是下标对应的数 for i, data := range args { fmt.Printf("args[%d] = %d\n", i, data) } } func main() { MyFunc(1, 2, 3) } ~~~ 参数间传递 ~~~ func MyFunc(args ...int) { //返回2个值一个是下标,一个是下标对应的数 for i, data := range args { fmt.Printf("args[%d] = %d\n", i, data) } fmt.Println("=============================") } func test(args ...int) { MyFunc(args...) //args[0]~args[2](不包括数字args[2]) MyFunc(args[:2]...) //从args[2]开始(包括本身),把后面所有元素传递过去 MyFunc(args[2:]...) } func main() { test(1, 2, 3) } ~~~ # 多个返回值 ~~~ func test() (int, int, int) { return 1, 2, 3 } func main() { a, b, c := test() fmt.Println(a, b, c) } ~~~ ~~~ func MaxAndMin(a, b int) (max, min int) { if a > b{ max = a min = b } else { max = b min = a } return } func main() { max, min := MaxAndMin(1, 2) //通过匿名变量丢弃某个返回值 //max, _ := MaxAndMin(1, 2) fmt.Print(max, min) } ~~~ # 函数类型 函数也是一种类型,我们可以用type来定义他,他的类型就是所有拥有相同的参数,相同的返回值的一种类型 ~~~ type FuncType func(int, int) int //声明一个函数类型, func后面没有函数名 func Add(a, b int) int { return a + b } func main() { var fTest FuncType fTest = Add result := fTest(10, 20) fmt.Print(result) } ~~~ # 函数作为参数 传递的函数要形参和返回值类型个数一样就可以 ~~~ func sub(a, b int) int { return a-b } func calc(a, b int, op func(int, int)int) int { return op(a, b) } func main() { sum := calc(300, 200, sub) fmt.Println(sum) } ~~~ # 回调函数 回调函数,函数里面有一个参数是函数类型 ~~~ type FuncType func(int, int) int //声明一个函数类型, func后面没有函数名 func Add(a, b int) int { return a + b } func Calc(a, b int, fTest FuncType) (result int) { fmt.Println("calc") result = fTest(a, b) return } func main() { a := Calc(1, 2, Add) fmt.Println("a = ", a) } ~~~ # defer **如果有return,就是在reutrn之前执行** 延迟调用,main函数结束前 ~~~ func main() { defer fmt.Println("bbbbbb") fmt.Println("aaaaaaaa") } ~~~ 输出 ~~~ aaaaaaaa bbbbbb ~~~ 如果一个函数有多个defer,他们会以**后进先出**顺序执行,哪怕某个函数或某个延迟调用发生错误,这些调用依旧会被执行 ~~~ func test(x int) int { a := 100 / x return a } func main() { fmt.Println("aaaaaaaa") defer fmt.Println("bbbbbb") defer test(0) defer fmt.Println("CCCCC") defer fmt.Println("DDDDDD") } ~~~ 输出 ~~~ aaaaaaaa DDDDDD CCCCC bbbbbb panic: runtime error: integer divide by zero ~~~ **defer和匿名函数结合使用** ~~~ func main() { a := 10 b := 20 defer func() { a = 12 b = 12 fmt.Printf("内部a = %d, b = %d\n", a, b) }() fmt.Printf("外部部a = %d, b = %d\n", a, b) } ~~~ 输出 ~~~ 外部部a = 10, b = 20 内部a = 12, b = 12 ~~~ ~~~ func main() { a := 10 b := 20 defer func(a, b int) { fmt.Printf("内部a = %d, b = %d\n", a, b) }(a, b) //相当于提前放10和20 a = 11 b = 21 fmt.Printf("外部部a = %d, b = %d\n", a, b) } ~~~ 输出 ~~~ 外部部a = 11, b = 21 内部a = 10, b = 20 ~~~ # 获取命令行参数 ~~~ func main() { //接收用户传递的参数,都是以字符串方式传递 list := os.Args //长度 n := len(list) fmt.Println(n) for i, data := range list { fmt.Printf("args[%d] = %s\n", i, data) } } ~~~ # 函数签名 在 Go 语言中,函数可是一等的(first-class)公民,函数类型也是一等的数据类型。这是什么意思呢? 简单来说,这意味着函数不但可以用于封装代码、分割功能、解耦逻辑,还可以化身为普通的值,在其他函数间传递、赋予变量、做类型判断和转换等等,就像切片和字典的值那样。 而更深层次的含义就是:函数值可以由此成为能够被随意传播的独立逻辑组件(或者说功能模块)。 ![](https://box.kancloud.cn/03e73f14c70b0155066144ae9354b7c2_954x590.png) 这里,我先声明了一个函数类型,名叫Printer。 注意这里的写法,在类型声明的名称右边的是func关键字,我们由此就可知道这是一个函数类型的声明。 在func右边的就是这个函数类型的参数列表和结果列表。其中,参数列表必须由圆括号包裹,而只要结果列表中只有一个结果声明,并且没有为它命名,我们就可以省略掉外围的圆括号。 书写函数签名的方式与函数声明的是一致的。只是紧挨在参数列表左边的不是函数名称,而是关键字func。这里函数名称和func互换了一下位置而已。 > 函数的签名其实就是函数的参数列表和结果列表的统称,它定义了可用来鉴别不同函数的那些特征,同时也定义了我们与函数交互的方式。 注意,各个参数和结果的名称不能算作函数签名的一部分,甚至对于结果声明来说,没有名称都可以。 只要两个函数的参数列表和结果列表中的元素顺序及其类型是一致的,我们就可以说它们是一样的函数,或者说是实现了同一个函数类型的函数。 严格来说,函数的名称也不能算作函数签名的一部分,它只是我们在调用函数时,需要给定的标识符而已。 我在下面声明的函数printToStd的签名与Printer的是一致的,因此前者是后者的一个实现,即使它们的名称以及有的结果名称是不同的。