原则

  • 函数与变量名首字母小写,本包可见,首字母大写,整个项目可见
  • 同包内函数与变量名不能重复

    导入

    再引入自己的包时,是从src的下一层开始,前提是当前项目路径在GOPATN中 ```go
  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函数()。

    1. <a name="uaJsD"></a>
    2. ## 定义函数
    3. ```go
    4. func name(参数 类型,...) 返回值类型{
    5. }
    6. 例1:
    7. func sum(i int,n int )int{
    8. fmt.Println(i+n)
    9. return i+n
    10. }
    11. 例2:定义返回值变量
    12. func sum(i int,n int )(m int){
    13. fmt.Println(i+n)
    14. m = i+n
    15. return//定义了返回值变量,return后就不用接语句了
    16. }

    函数的返回值

  • go支持多个返回值,用逗号隔开即可 ```go func sum_sub(i int,n int )int{ sum := i+n sub := i-n return sum,sub }

func main(){ sum,sub :=sumsub(1,2) //如果只想要其中一个,另一个用‘’占位即可 sum,_ :=sum_sub(1,2) }

  1. <a name="ELcTV"></a>
  2. ## 传参
  3. - **_值类型:_**_基本数据类型int系列, float系列, bool, string 、数组和结构体struct_
  4. - **_引用类型:_**_指针、slice切片、map、管道chan, interface等都是引用类型,传的是地址_
  5. ```go
  6. func fun(i string){
  7. i+="b"
  8. }
  9. func fun2(i *string){
  10. *i+="b"
  11. }
  12. func main(){
  13. i:="a"
  14. fun(i)//基本类型传递的是值,如果函数中对值发生了变化,这里的i是不会变的
  15. fmt.Println(i)//a
  16. n :="a"
  17. fun2(&n)//这里传递的是n的地址,
  18. fmt.Println(n) //ab
  19. }

将函数作为参数传递

  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. )
  6. type Student struct {
  7. Name string
  8. Age int
  9. IsMan bool
  10. Score float64
  11. }
  12. // 定义一个接收一个Student类型参数的函数类型
  13. type handle func(str Student) string
  14. // exec函数,接收handle类型的参数
  15. func exec(f handle) string{
  16. s :=Student{"jw",12,true,100}
  17. return f(s)
  18. }
  19. func fun(s Student) string{
  20. data,_ :=json.Marshal(s)
  21. return string(data)
  22. }
  23. func main() {
  24. //带名函数
  25. stu_1 :=exec(fun)
  26. // 匿名函数作为参数直接传递给exec函数
  27. stu_2 :=exec(func(s Student) string{
  28. data,_ :=json.Marshal(s)
  29. return string(data)
  30. })
  31. fmt.Println(stu_1)
  32. fmt.Println(stu_2)
  33. }

一种高可用的设计模式

  1. package main
  2. import "fmt"
  3. type Options struct {
  4. StringOption1 string
  5. StringOption2 string
  6. IntOption1 int
  7. IntOption2 int
  8. }
  9. type Option func(opt *Options)
  10. func InitOption(opt ...Option){
  11. options := &Options{}
  12. for _,opt :=range opt{
  13. opt(options)
  14. }
  15. fmt.Printf("%v",*options)
  16. }
  17. func WithStringOption1(str string)Option{
  18. return func(opt *Options) {
  19. opt.StringOption1=str
  20. }
  21. }
  22. func WithStringOption2(str string)Option{
  23. return func(opt *Options) {
  24. opt.StringOption2=str
  25. }
  26. }
  27. func WithIntOption1(intvar int)Option{
  28. return func(opt *Options) {
  29. opt.IntOption1=intvar
  30. }
  31. }
  32. func WithIntOption2(intvar int)Option{
  33. return func(opt *Options) {
  34. opt.IntOption2=intvar
  35. }
  36. }
  37. func main() {
  38. InitOption(
  39. WithStringOption1("aaa"),
  40. WithStringOption2("bbb"),
  41. WithIntOption1(111),
  42. WithIntOption2(222),
  43. )
  44. }

不定参

  1. //arr相当于切片
  2. func test_sum(arr ...int) int{
  3. sum:=0
  4. for _,v :=range arr{
  5. sum+=v
  6. }
  7. return sum
  8. }
  9. func main() {
  10. sum:=test_sum(1,2,3)
  11. fmt.Println(sum)
  12. }

数组陷阱

  1. 语法错误:
  2. 注意:[]int是切片,而不是数组,[3]int才是数组类型
  3. func array(arr []int){
  4. fmt.Println(arr)
  5. }
  6. func main() {
  7. arr := [3]int{1,2,3}
  8. array(arr)
  9. }
  10. array(arr [4]int),这样定义也是错的,长度不一致,编译器会认为它们数据类型不一样
  11. array(arr [...]int),这样定义也是错的,必须明确指定数组长度

匿名函数

  1. //全局匿名函数
  2. var(
  3. Fun1 = func (i int,n int) int{
  4. return i*n
  5. }
  6. )
  7. func main() {
  8. 方式一:
  9. sum :=func (i int,n int) int{
  10. return i+n
  11. }(1,2)
  12. fmt.Println(sum)//3
  13. 方式二:
  14. sub := func (i int,n int) int{
  15. return i-n
  16. }
  17. res:=sub(1,2)
  18. fmt.Println(res)//-1
  19. 方式三:全局匿名函数
  20. res1:=Fun1(2,3)
  21. fmt.Println(res1)//6
  22. }

闭包

  1. //闭包
  2. func run() func(int) int{
  3. i:=10//外层包的数据只会初始化一次
  4. str:="hello"
  5. return func(n int) int {
  6. i+=n
  7. newn :=strconv.FormatInt(int64(i),10)
  8. str+=newn
  9. fmt.Println(str)
  10. /*
  11. hello11
  12. hello1112
  13. hello111213
  14. */
  15. return i
  16. }
  17. }
  18. func main() {
  19. f:=run()
  20. fmt.Println(f(1)) //11
  21. fmt.Println(f(1))//12
  22. fmt.Println(f(1))//13
  23. }

函数体内可修改全局变量

  1. var name = "张三"
  2. //不能使用 name :="张三" 这样去定义
  3. func test1(){
  4. name = "李四"//修改值,对全局变量有影响
  5. name := "李四"//重新定义name,对全局没影响,且语法正确
  6. fmt.Println(name)
  7. }
  8. func main() {
  9. test1()
  10. fmt.Println(name)//李四 李四
  11. }
  12. //将test1中的name="李四"改为,name :="李四"
  13. //李四
  14. //张三

函数的执行顺序

  1. import (
  2. "fmt"
  3. u"函数/utlis"
  4. )
  5. var age = test()
  6. func test()int{
  7. fmt.Println("test函数执行了")
  8. return 1
  9. }
  10. //init函数会在main函数之前执行,通常用于初始化
  11. func init() {
  12. fmt.Println("init函数执行了")
  13. }
  14. func main(){
  15. fmt.Println("main函数执行了")
  16. fmt.Println(age)
  17. fmt.Println(u.Name)
  18. }
  19. /*
  20. utlis包中的init函数执行了
  21. test函数执行了
  22. init函数执行了
  23. main函数执行了
  24. 1
  25. 张三
  26. */

defer

  1. func sum1(i int,n int) int{
  2. //defer语句,暂时不执行,而是当所在函数执行完之后再执行,会将defer后的语句压入到栈中
  3. //当函数执行完后,会以LIFO的方式出栈并开始执行,通常用于收尾工作
  4. defer fmt.Println("i=",i)
  5. defer fmt.Println("n=",n)
  6. i++//注意在defer之后对值发生变化,对已经压入站的defer语句没影响
  7. n++
  8. res:=i+n
  9. fmt.Println("计算完成")
  10. return res
  11. }
  12. func main() {
  13. res := sum1(10,20)
  14. fmt.Println(res)
  15. }
  16. /*
  17. 计算完成
  18. n= 20
  19. i= 10
  20. 32
  21. */

使用陷阱

  1. func f1() (t int){
  2. defer func() {
  3. t++
  4. }()
  5. return 0
  6. }
  7. func f2() (r int){
  8. t:=5
  9. defer func() {
  10. t++
  11. }()
  12. return t
  13. }
  14. func f3() (r int){
  15. defer func(r int) {
  16. r++
  17. }(r)
  18. return r
  19. }
  20. func main() {
  21. res1 :=f1()//1
  22. res2 :=f2()//5
  23. res3 :=f3()//0
  24. }

解析:f1

  • 带名返回值,t被赋值为零值:0
  • return 0,复制0到返回值栈区,返回值t被赋值为0
  • defer ,由于匿名函数是对t的闭包引用(地址一样),t++后,t=1,因此函数返回值被修改为1

解析:f2

  • 带名返回值,r被赋值为零值:0
  • 定义局部变量t,变并初始化为5
  • return t,复制t的值到返回值栈区,返回值r被赋值为5
  • defer t++,由于匿名函数是对t的闭包引用,t++后,t=6,此时返回值栈区变量为r,且值是5,所以函数返回值为5

解析:f3

  • 带名返回值,r被赋值为零值:0
  • return r = return 0,复制0到返回值栈区,返回值r被赋值为0
  • defer r++ ,此时匿名函数是带参数的,参数为r,此时会复制f3中的r值:0传给匿名函数,经过r++后,匿名函数中的r值修改为1,注意此时f3中与匿名函数中的变量r的地址是不一样的,因此函数返回0

例子1

  1. func f3() (r int){
  2. defer func(r *int) {
  3. *r++
  4. }(&r)//传地址
  5. return r
  6. }
  7. res3 :=f3()//1,

例子2

  1. func f3() (int){
  2. var r int
  3. defer func(r *int) {
  4. *r++
  5. }(&r)
  6. return r
  7. }
  8. res3 :=f3()//0,

例子3

  1. package main
  2. import "fmt"
  3. type A struct {
  4. A1 int64
  5. }
  6. func main() {
  7. fmt.Printf("%d\n",test1().A1) // 100
  8. fmt.Printf("%d\n",test2().A1) // 200
  9. }
  10. func test1()(A){
  11. var a A
  12. defer func() {
  13. a.A1 = 200
  14. }()
  15. a.A1 = 100
  16. return a
  17. }
  18. func test2()(*A){
  19. var a A
  20. defer func() {
  21. a.A1 = 200
  22. }()
  23. a.A1 = 100
  24. return &a
  25. }

例子4

  1. package main
  2. import "fmt"
  3. type A struct {
  4. A1 int64
  5. }
  6. func main() {
  7. fmt.Printf("%d\n",test1().A1) // 100
  8. fmt.Printf("%d\n",test2().A1) // 200
  9. fmt.Printf("%d\n",test3().A1) // 200
  10. }
  11. func test1()(A){
  12. var a A
  13. defer func() {
  14. a.A1 = 200
  15. }()
  16. a.A1 = 100
  17. return a
  18. }
  19. func test2()(*A){
  20. var a A
  21. defer func() {
  22. a.A1 = 200
  23. }()
  24. a.A1 = 100
  25. return &a
  26. }
  27. func test3()(a A){
  28. defer func() {
  29. a.A1 = 200 // 这里的a是对返回值栈区的闭包引用(地址一样)
  30. }()
  31. a.A1 = 100
  32. return a
  33. }