go 闭包,延迟调用

go 闭包,延迟调用,第1张

go函数

• 无需声明原型。
• 支持不定 变参。
• 支持多返回值。
• 支持命名返回参数。
• 支持匿名函数和闭包。
• 函数也是一种类型,一个函数可以赋值给变量。

• 不支持 嵌套 (nested) 一个包不能有两个名字一样的函数。
• 不支持 重载 (overload)
• 不支持 默认参数 (default parameter)。

传参

在默认情况下,Go 语言使用的是值传递,即在调用过程中不会影响到实际参数。

注意1:无论是值传递,还是引用传递,传递给函数的都是变量的副本,不过,值传递是值的拷贝。引用传递是地址的拷贝,一般来说,地址拷贝更为高效。而值拷贝取决于拷贝的对象大小,对象越大,则性能越低。

注意2:map、slice、chan、指针、interface默认以引用的方式传递。

不定参数

不定参数传值 就是函数的参数不是固定的,后面的类型是固定的。(可变参数)

Golang 可变参数本质上就是 slice。只能有一个,且必须是最后一个。

在参数赋值时可以不用用一个一个的赋值,可以直接传递一个数组或者切片,特别注意的是在参数后加上“…”即可。

  func myfunc(args ...int) {    //0个或多个参数
  }

  func add(a int, args…int) int {    //1个或多个参数
  }

  func add(a int, b int, args…int) int {    //2个或多个参数
  }
  注意:其中args是一个slice,我们可以通过arg[index]依次访问所有参数,通过len(arg)来判断传递参数的个数.
闭包、递归

闭包是由函数及其相关引用环境组合而成的实体(即:闭包=函数+引用环境)。

func test() func() {
	x := 100
	fmt.Printf("x (%p) = %d\n", &x, x)

	return func() {
		fmt.Printf("x (%p) = %d\n", &x, x)
	}
}

func main() {
	test()()
}

在汇编层 ,test 实际返回的是 FuncVal 对象,其中包含了匿名函数地址、闭包对象指针。当调 匿名函数时,只需以某个寄存器传递该对象即可。

外部引用函数参数局部变量
func add(base int) func(int) int {
	return func(i int) int {
		base += i
		return base
	}
}
func main() {
	tmp1 := add(10)
	// 0x7cbda0 func(int) int
	fmt.Printf("%v %T \n",tmp1,tmp1)
	fmt.Println(tmp1(1))
	fmt.Printf("%v %T \n",tmp1,tmp1)
	fmt.Println(tmp1(2))
	tmp2 := add(100)
	fmt.Printf("%v %T \n",tmp1,tmp1)
	fmt.Println(tmp2(1))
	fmt.Printf("%v %T \n",tmp1,tmp1)
	fmt.Println(tmp2(2))
}
------------------------------------
// base一直存在,不断被修改

0x10bbf20 func(int) int
11
0x10bbf20 func(int) int
13
0x10bbf20 func(int) int
101
0x10bbf20 func(int) int
103
延迟调用(defer)

defer 是先进后出

这个很自然,后面的语句会依赖前面的资源,因此如果先前面的资源先释放了,后面的语句就没法执行了。

defer 碰上闭包
func main() {
    var whatever [5]struct{}
    for i := range whatever {
        defer func() { fmt.Println(i) }()
    }
}
------------------------------
4
4
4
4
4

Each time a “defer” statement executes, the function value and parameters to the call are evaluated as usualand saved anew but the actual function is not invoked.

也就是说函数正常执行,由于闭包用到的变量 i 在执行的时候已经变成4,所以输出全都是4.

defer后面的语句在执行的时候,函数调用的参数会被保存起来,但是不执行。也就是复制了一份。

多个defer

多个 defer 注册,按 FILO 次序执行 ( 先进后出 )。哪怕函数或某个延迟调用发生错误,这些调用依旧会被执行。

package main

func test(x int) {
    defer println("a")
    defer println("b")

    defer func() {
        println(100 / x) // div0 异常未被捕获,逐步往外传递,最终终止进程。
    }()

    defer println("c")
}

func main() {
    test(0)
}
-----------------------------------------
c
b
a
panic: runtime error: integer divide by zero
defer和return

go的return语句不是原子性的

func foo() (i int) {

    i = 0
    defer func() {
        fmt.Println(i)
    }()

    return 2
}

func main() {
    foo()
}
-----------------------------------
2
return 2 相当于 i = 2执行defer语句 fmt.Println(i)return i defer nil 函数
func test() {
    var run func() = nil
    // 相当于 nil()
    defer run()
    fmt.Println("runs")
}

func main() {
    defer func() {
        if err := recover(); err != nil {
            fmt.Println(err)
        }
    }()
    test()
}
-------------------------------------
runs
runtime error: invalid memory address or nil pointer dereference

名为 test 的函数一直运行至结束,然后 defer 函数会被执行且会因为值为 nil 而产生 panic 异常

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/996231.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-21
下一篇 2022-05-21

发表评论

登录后才能评论

评论列表(0条)

保存