[go]-golang闭包简介与变量引用

文章目录

  • 函数
    • 匿名函数
  • 闭包
    • 引用变量
    • 记忆效应
    • 循环中闭包捕获外部变量的坑

函数在golang中是一等公民,闭包可以看成函数的高阶应用,是golang高级开发的必备技能。

函数

函数是一等公民(First-class value),即函数可以作为另一个函数的返回值或参数,还可以作为一个变量的值。

匿名函数

匿名函数跟普通函数是一样的,只是他没有名字。

直接使用的匿名函数:

sum := func(first int, second int) int {
	return first + second
}(5, 10)

fmt.Println("result: ", sum)

将函数赋值给变量,然后调用:

sumFun := func(first int, second int) int {
	return first + second
}
fmt.Println("result: ", sumFun(5, 10))

闭包

闭包是指内层函数引用了外层函数中的变量(或称为引用了自由变量)的函数,其返回值也是一个函数。闭包只是在形式和表现上像函数,但实际上不是函数。函数是一些可执行的代码,这些代码在函数被定义后就确定了,不会在执行时发生变化,所以一个函数只有一个实例。闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。函数是编译期静态的概念,而闭包是运行期动态的概念。

闭包=函数+引用环境(是指在程序执行中的某个点所有处于活跃状态的约束所组成的集合)。

[go]-golang闭包简介与变量引用_第1张图片

引用变量

闭包对环境中变量的引用过程也可以被称为“捕获”。闭包对它作用域上部的变量可以进行修改,修改引用的变量会对变量进行实际修改。

outStr := "outer-string"
foo := func() {
	outStr = "inner-string"
}

fmt.Println("before: ", outStr)
foo()
fmt.Println("after: ", outStr)

// before:  outer-string
// after:  inner-string

记忆效应

被捕获到闭包中的变量让闭包本身拥有了记忆效应,闭包中的逻辑可以修改闭包捕获的变量,变量会跟随闭包生命期一直存在,闭包本身就如同变量一样拥有了记忆。

func counter(initValue int) func() int  {
	return func() int {
		initValue++
		return initValue
	}
}

	count := counter(0)
	for i:=1; i<10; i++{
		fmt.Println(count())
	}
	// 输出:1、2...9

循环中闭包捕获外部变量的坑

闭包捕获对外部变量是通过引用的方式实现的;会随着外部变量的改变而修改。为避免此问题可:

  • 通过参数方式传入外部变量;
  • 定义局部变量的方式;
func delayPrint() {
    // 通过参数方式保证每个变量值是不同的;
	for i := 0; i < 3; i++ {
		go func(i int) {
			time.Sleep(time.Second * 1)
			fmt.Println("By param: ", i)
		}(i)
	}
	time.Sleep(time.Second * 4)
	
    // 直接引用外部变量,会发现所有调用最终都捕获了同一个变量值
	for i := 0; i < 3; i++ {
		go func() {
			time.Sleep(time.Second * 1)
			fmt.Println("By clouser: ", i)
		}()
	}
	time.Sleep(time.Second * 4)

    // 通过引入局部变量方式,保证捕获的变量是不同的
	for i := 0; i < 3; i++ {
		tmp := i
		go func() {
			time.Sleep(time.Second * 1)
			fmt.Println("By tmp: ", tmp)
		}()
	}
	time.Sleep(time.Second * 4)
}

// By param:  2
// By param:  0
// By param:  1
// By clouser:  3
// By clouser:  3
// By clouser:  3
// By tmp:  0
// By tmp:  2
// By tmp:  1

你可能感兴趣的:(Go,golang,闭包,函数)