golang-闭包实例详解--未完

孤人 提交于 2020-02-28 17:54:38

闭包

概念

闭包函数:声明在一个函数中的函数,叫做闭包函数。

闭包:内部函数总是可以访问其所在的外部函数中声明的参数和变量,即使在其外部函数被返回(寿命终结)了之后。

特点

让外部访问函数内部变量成为可能;

局部变量会常驻在内存中;

可以避免使用全局变量,防止全局变量污染;

会造成内存泄漏(有一块内存空间被长期占用,而不被释放)

闭包的创建

闭包就是可以创建一个独立的环境,每个闭包里面的环境都是独立的,互不干扰。闭包会发生内存泄漏,**每次外部函数执行的时候,外部函数的引用地址不同,都会重新创建一个新的地址。**但凡是当前活动对象中又被内部子集引用的数据,那么这个时候,这个数据不删除,保留一根

闭包实例

示例一

  • 返回一个内部函数
package main

import "fmt"

//函数片段
func add(base int) func(int) int { // 1:创建一个新内存, 其地址并命名为base用来存储int变量, 然后取出a内存处存放的值,复制一份,放入base内存

	fmt.Printf("%p\n", &base)  //2:打印变量地址0xc0420080e0, 里面存储着10【它是函数拷贝传值】

	f := func(i int) int {  //3:定义一个函数,并且用f指向它[f里面存储着这个函数的地址]
	    //7、将1用i存储起来
		fmt.Printf("%p\n", &base) // 2:打印变量地址0xc0420080e0
		base += i // 取出base里面存储的值 + i里面存储的值,然后将结果放入base存储起来
		return base //将base里面存储的值返回
	}

	return f  // 4:将函数地址f返回
}



func main() {
	aa := 10
	fmt.Printf("a的地址:%p\n", &aa) //0xc0420080a8
	t1 := add(aa)                //5、将add(a)返回的函数地址用一个t1存储起来
	c := t1(1)                   //6、调用t1指向的函数,传参为1[传值],然后将返回结果用新建的内存c存储起来
	fmt.Println(c, &c)           //11 0xc0420540b0
	fmt.Println(t1(2))           // 13  t1还是指向原来开辟的空间,此时base=11, 因此11+2

	t2 := add(100) //重新开辟一个空间调用add(100),然后将返回的函数地址用一个t2存储起来,此时base为新建立的
	fmt.Println(t2(1), t2(2))
}

  • 返回多个内部函数
package main

import "fmt"

//返回加减函数,重点:内部函数时对外部变量的引用
func calc(base int) (func(int) int, func(int) int) {

	fmt.Printf("%p\n", &base)
	add := func(i int) int {
		fmt.Printf("%p\n", &base)
		base += i
		return base
	}

	sub := func(i int) int {
		fmt.Printf("%p\n", &base)
		base -= i
		return base
	}

	return add, sub
}

//由 main 函数作为程序入口点启动
func main() {
	f1, f2 := calc(11111)
	fmt.Println(f1(1), f2(1)) //执行顺序:f1 f2 println
}

示例二

涉及 goroutine 时的情况,程序片段如下所示:

package main

import (
	"fmt"
	"time"
)

//由 main 函数作为程序入口点启动
func main() {

	for i:=0; i<5; i++ {
		go func(){
			fmt.Println(i) //i变量值也是引用.创建5个线程执行函数, for循环执行过程中可能执行完的时候,线程刚好处于i的某个值。
		}()

	}
	time.Sleep(time.Second * 1)
}

每次程序的运行结果都是不一样的
在这里插入图片描述
代码改进:

package main

import (
	"fmt"
	"time"
)

func main() {

	ch := make(chan int, 1)

	for i:=0; i<5; i++ {
		go func(){
			ch <- 1
			fmt.Println(i)
		}()

		<- ch
	}
	time.Sleep(time.Second * 1)
}

会阻塞等待:0-4值一定有
在这里插入图片描述
参考:https://blog.csdn.net/li_101357/article/details/80196650

实例三:

package main

import "fmt"

func funA() func(){
	var a = 10;
	return func (){
		fmt.Println(a); // 打印处a存储的值
	}
}


func main() {
	var b = funA() //funA()返回一个函数地址,执行一个函数,并用b保存这个地址,此时b指向这个函数
	b() // 调用b指向的函数
}

示例四:全局变量VS闭包函数

package main

import "fmt"

func Outer() func(){
	var i = 0;
	return func() {
		i++;
		fmt.Println(i)
	}
}

func main(){
	var c = Outer()
	c()
	c()
	c()

	var d = Outer()
	d()
	d()
	d()
}

// 1 2 3 1 2 3 
  • 全局函数
package main

import "fmt"

var i = 0;


func Outer() func(){

	return func() {
		i++;
		fmt.Println(i)
	}
}

func main(){
	var c = Outer()
	c()
	c()
	c()

	var d = Outer()
	d()
	d()
	d()
}
// 1 2 3 4 5 6 

实例六

package main

import "fmt"

func Outer() func() int{
	var i = 0;
	return func() int{
		 i++;
		 return i;
	}
}

func main(){
	c := Outer() // 返回一个函数
	fmt.Println(c()) //1 调用内部函数,并且拷贝i的值,返回
	fmt.Println(c()) //2 调用内部函数,并且拷贝i的值,返回

	d := c // c里面存储着函数地址,c将存储的函数地址放入d中,此时d和c指向同一个函数
	fmt.Println(d()) //3 调用内部函数,并且拷贝i的值,返回
	fmt.Println(d()) //4 调用内部函数,并且拷贝i的值,返回

	e := Outer() // 新开辟一个内存,用来调用函数。 e存储着这个内存的地址
	fmt.Println(e()) //1 调用内部函数,并且拷贝i的值,返回
	fmt.Println(e()) //2 调用内部函数,并且拷贝i的值,返回

	fmt.Println(Outer()) //0x496a30
	fmt.Println(Outer()()) //1
}

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!