9.Go语言干货-函数进阶

永远不要高估自己

1. 变量作用域

1.1 全局变量

全局变量是定义在函数外部的变量,全局有效。函数体中可以访问到全局作用域的变量。

package main

import "fmt"
var num = 100
func f1(){
    fmt.Println(num) // 函数内部可以访问到全局作用域的变量
}
func main(){
    f1() 
}

1.2 局部变量

局部变量分为两种:1.函数体内部定义的变量。2.语句块中定义的变量

  1. 函数内部定义的变量

    func f1(){
        // 定义一个局部变量(函数内部),该变量只在该函数中有效。其他地方无法访问
        var x int64 = 100
        fmt.Println(x)
    }
    func main(){
        f1() // 100
        fmt.Println(x) // 报错:undefined: x
    }
    
    1. 如果全局作用域与局部作用域有变量重名,优先调用自己作用域的变量。

      package main
      import "fmt"
      var num int64 = 100
      func f1(){
          num:=99
          fmt.Println(num) // 99 优先调用自己作用域的变量
      }
      func main(){
          fmt.Println(num) // 100 自己作用域没有就去上一层找变量
      }
      
  2. 语句块中的变量,通常在if判断for循环switch 体现

    func f1(x,y int){
        fmt.Println(x,y)//函数的参数只能在函数内部生效
        if x >= 0{
            z := 100 // 变量z只能在if语句中生效
            fmt.Println(z)
        }
        fmt.Println(z) // 报错,无法使用变量z
    }
    // for 循环,switch里面也是一样的
    

2. 高阶函数

高阶函数分为函数作为参数和函数作为返回值两部分

2.1 函数作为参数

func add(x,y int)int{
    return x + y
}
func calc(x,y int,op func(int,int)int) int{
    return op(x,y)
}
func main(){
    ret := calc(8,9,add)
    fmt.Println(ret) // 17
}

2.2 函数作为返回值

func do(s string) (func(int,int) int,error){
    switch s {
	case "+":
		return add, nil
	case "-":
		return sub, nil
	default:
		err := errors.New("无法识别的操作符")
		return nil, err
	}
}

2.3 匿名函数与闭包

  1. 匿名函数

    1. 把函数作为返回值,但是在Go语言中函数内部不能定义有名函数,所以可以在函数内定义匿名函数。

    2. 匿名函数格式

      func(参数)(返回值){
          函数体
      }
      
    3. 匿名函数没有函数名,所以不能像调用普通函数那样调用,可以将匿名函数赋值给一个变量通过调用变量来执行匿名函数。

      func main(){
          //将函数赋值给变量
          add := func(x,y int){
              fmt.Println(x+y)
          }
          add(10,20) // 通过变量调用匿名函数
          
          // 自执行函数,匿名函数定义完加()直接执行函数
          func(x,y int){
              fmt.Println(x+y)
          }(10,20)
      }
      
    4. 匿名函数多用于回调函数或者闭包函数

  2. 闭包函数

    1. 闭包指的是一个函数和与其相关的引用环境组合而成的实体
    func adder() func(int) int{
        var x int
        return func(y int) int {
            x += y
            return x
        }
    }
    func main(){
        var f = adder()
        fmt.Println(f(10))
        fmt.Println(f(20))
        fmt.Println(f(30))
        
        f1 := adder()
        fmt.Println(f1(50))
        fmt.Println(f1(60))
    }
    
    1. 变量f是一个函数并且引用了其他外部作用域的变量x,此时f就是一个闭包。在f的生命周期内,变量x也是一直有效

      func adder(x int)func(int) int{
          return func(y int) int{
              x+=y
              return x
          }
      }
      
      func main(){
          var f = adder2(10)
      	fmt.Println(f(10)) //20
      	fmt.Println(f(20)) //40
      	fmt.Println(f(30)) //70
      
      	f1 := adder2(20)
      	fmt.Println(f1(40)) //60
      	fmt.Println(f1(50)) //110
      }
      
2.3.1 闭包示例
func makeSuffixFunc(suffix string) func(string) string{
    return func(name string) string{
        if !strings.HasSuffix(name,suffix){
            return name + suffix
        }
        return name
    }
}

func main(){
    jpgFunc := makeSuffixFunc(".jpg")
    txtFunc := makeSuffixFunc(".txt")
    fmt.Println(jpgFunc("test"))
    fmt.Println(txtFunc("test"))
}
func calc(base int) (func(int) int,func(int) int){
    add := func(i int) int{
        base += i
        return base
    }
    sub := func(i int) int{
        base -= i
        return base
    }
    return add,sub
}

func main(){
    f1, f2 := calc(10)
	fmt.Println(f1(1), f2(2)) //11 9
	fmt.Println(f1(3), f2(4)) //12 8
	fmt.Println(f1(5), f2(6)) //13 7
}

3.derfer语句(延迟调用)

Go语言中的defer语句会将后面紧跟的语句进行延迟处理。在defer归属的函数即将返回时,延迟处理的语句按defer定义的逆序进行执行

举个栗子

func main(){
    fmt.Println("start")
    defer fmt.Println(1)
    defer fmt.Println(2)
    defer fmt.Println(3)
    fmt.Println("end")
}
// 执行结果
start
end
3
2
1

由于defer语句延迟调用的特性,所以defer语句能非常方便的处理资源释放问题。比如:资源清理、文件关闭、解锁及记录时间等。

3.1 defer执行的时机

defer执行时机

3.2 defer经典案例

func f1() int {
	x := 5
	defer func() {
		x++
	}()
	return x
}

func f2() (x int) {
	defer func() {
		x++
	}()
	return 5
}

func f3() (y int) {
	x := 5
	defer func() {
		x++
	}()
	return x
}
func f4() (x int) {
	defer func(x int) {
		x++
	}(x)
	return 5
}
func main() {
	fmt.Println(f1())
	fmt.Println(f2())
	fmt.Println(f3())
	fmt.Println(f4())
}

3.3 defer 面试题

package main

import "fmt"

func calc(index string, a, b int) int {
	ret := a + b
	fmt.Println(index, a, b, ret)
	return ret
}

func main() {
	x := 1
	y := 2
	defer calc("AA", x, calc("A", x, y))
	x = 10
	defer calc("BB", x, calc("B", x, y))
	y = 20
}

4.内置函数

内置函数功能介绍
close主要用来关闭channel
len用来求长度,比如string、array、slice、map、channel
new用来分配内存,主要用来分配值类型,比如int、struct。返回的是指针
make用来分配内存,主要用来分配引用类型,比如chan、map、slice
append用来追加元素到数组、slice中
panic和recover用来做错误处理

4.1 panic、recover

Go语言中目前(Go1.12)是没有异常机制,但是使用panic/recover模式来处理错误。 panic可以在任何地方引发,但recover只有在defer调用的函数中有效。 首先来看一个例子:

func funcA() {
	fmt.Println("func A")
}

func funcB() {
	panic("panic in B")
}

func funcC() {
	fmt.Println("func C")
}
func main() {
	funcA()
	funcB()
	funcC()
}

输出:

func A
panic: panic in B

goroutine 1 [running]:
main.funcB(...)
        .../code/func/main.go:12
main.main()
        .../code/func/main.go:20 +0x98

程序运行期间funcB中引发了panic导致程序崩溃,异常退出了。这个时候我们就可以通过recover将程序恢复回来,继续往后执行。

func funcA() {
	fmt.Println("func A")
}

func funcB() {
	defer func() {
		err := recover()
		//如果程序出出现了panic错误,可以通过recover恢复过来
		if err != nil {
			fmt.Println("recover in B")
		}
	}()
	panic("panic in B")
}

func funcC() {
	fmt.Println("func C")
}
func main() {
	funcA()
	funcB()
	funcC()
}

注意:

  1. recover()必须搭配defer使用。
  2. defer一定要在可能引发panic的语句之前定义。
 funcA() {
	fmt.Println("func A")
}

func funcB() {
	defer func() {
		err := recover()
		//如果程序出出现了panic错误,可以通过recover恢复过来
		if err != nil {
			fmt.Println("recover in B")
		}
	}()
	panic("panic in B")
}

func funcC() {
	fmt.Println("func C")
}
func main() {
	funcA()
	funcB()
	funcC()
}

注意:

  1. recover()必须搭配defer使用。
  2. defer一定要在可能引发panic的语句之前定义。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值