golang中匿名函数与闭包的前世今生

155 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第4天,点击查看活动详情

golang中的匿名函数与闭包用法及关系

前情提要,什么是函数?

在不同的语言中,有的叫做函数比如C系列语言。有的则叫做方法,比如java语言。不过都只是叫法不同,但功能都是相同的。在Go语言中,函数作为一等公民进行使用。那么什么是一等公民呢?这个概念过于抽象,我简而述之。

一等公民

在维基百科中一等公民有如下解释:

In programming language design, a first-class citizen (also typeobjectentity, or value) in a given programming language is an entity which supports all the operations generally available to other entities. These operations typically include being passed as an argument, returned from a function, modified, and assigned to a variable.

我的理解是:函数在该语言中具有和类型、对象、实例化、变量具有同等效应,同等级别的地位。大意是说,在编程语言中,所谓一等公民,是指支持所有操作的实体, 这些操作通常包括作为参数传递,从函数返回,修改并分配给变量等。如golang中int可以作为参数传递,类型赋值给变量。而golang中的函数也有如此功能,也能够作为参数进行传递,也能够进行类型赋值给变量。所以函数被称为一等公民

什么是匿名函数?

在了解了函数之后,我们先不解释什么是匿名函数,我们先需要知道函数的用法以及常见的具名函数,看下例即可了解!

func add(i int) int {
   sum := 0
   for i := 1; i < 10; i++ {
      sum += i
   }
   return sum
}

func main() {
   add(1)
}

上述代码中总共有两个函数,main函数add函数。其中main函数是一个特殊的函数体,他不能有返回值和参数,并且函数名称也必须固定,他是一个程序的入口函数。add函数则是一个普通的具名函数,可以进行参数传递和参数返回,可以对一些功能进行封装,方便调用,提高开发效率。说了这么半天,那么匿名函数到底是什么呢?

匿名函数与普通函数的区别

一般来说前端开发者对于匿名函数一定不陌生。在golang中匿名函数其实也是如此,匿名匿名,就是把名称藏起来的函数。

func(i int) int {
   sum := 0
   for i := 1; i < 10; i++ {
      sum += i
   }
   return sum
}(1)

上述这个例子就是一个匿名函数。没有名称,但是可以传递参数,可以返回参数,执行和具名函数同等功能的作用,甚至于比具名函数更加简便(绝对不是因为起名困难[doge])。匿名函数的参数传递需要在行末进行形成闭包。或者不在此处传递参数,而是赋值给一个变量(因为函数是一等公民)。

a := func(i int) int {
   sum := 0
   for i := 1; i < 10; i++ {
      sum += i
   }
   return sum
}
a(1)

在golang中,匿名函数通常被用于开启一个goroutine,或者是在defer语句之后被执行。

go func() {
   // -- TODO
}()
==========================================================================================
defer func(){
    // -- TODO
}()

什么是闭包?

闭包(Closure)是匿名函数的一个特例。当一个匿名函数所访问的变量定义在函数体的外部时,就称这样的匿名函数为闭包。听不懂吗?没关系,直接上案例一看就明白!

func sayHello() func(string) string {
   t := "Hi"
   c := func(b string) string {
      t = t + " " + b
      return t
   }
   return c
}
func main() {
   say := sayHello()
   fmt.Println(sayHello()("golang!"))
   fmt.Println(say("golang!"))
}
输出结果:
Hi golang!
Hi golang!

一般闭包是在函数体内形成的。sayHello函数的返回值为一个匿名函数,并且该匿名函数中使用了sayHello函数的变量t,因此这个函数就构成了闭包。如果还是不理解就看main函数中的执行语句。sayHello函数的返回值作为一个变量赋值给了say。所以它可以使用say("golang!"),也可以使用sayHello()("golang!")进行调用。而这两个方式皆触发了闭包效果。即函数调用函数,在函数内部的变量可以作为调用函数的变量来使用。

闭包的练习

package main

import "fmt"

func app() func(string) string {
   t := "Hi"
   c := func(b string) string {
      t = t + " " + b
      return t
   }
   return c
}

func main() {
   a := app()
   b := app()
   a("go")
   fmt.Println(b("All"))
}

首先看这个例子要知道app作为一个函数,实际上是一个闭包函数。因为返回值函数内部调用了函数内部的变量,形成了闭包效果。另外,a和b虽然都是app函数,但实际上作用域并不相同。他们不享有共同空间。因此a调用和b调用的结果必然不同!

答案为: Hi All你是否答对了呢?

闭包的用法多种多样,此处只是作为练习列举了其中一种。如果你想深一步研究,可以自行探索!