Golang 中的匿名函数和闭包

552 阅读3分钟
匿名函数

Golang支持匿名函数,就是在定义函数时只有函数体没有函数名,并且函数可以赋值给变量,作为其他函数的参数进行传递;下面是一个匿名函数的例子:

package main
​
import "fmt"func main() {
    // 创建一个匿名函数并且赋值给一个变量
    f := func(a,b int) int {
        return a+b
    }
    // 调用匿名函数
    fmt.Println(f(2,3))
}

使用匿名函数作为回调函数来获取到一个数组当中的一个最大值,如下:

import "fmt"func main() {
    vals := []int{2,6,12,1,3}
​
    var maxVal int
    for _,v := range vals{
        //此处传入比较的具体值和函数
        if !compare(maxVal,v, func(a, b int) bool {
            return a>=b
        }){
            maxVal = v
        }
    }
    fmt.Printf("The max value :%d",maxVal)
}
​
//比较这两个值
func compare(c,d int,f func(a, b int) bool) bool {
    return f(c,d)
}
闭包

闭包就是匿名函数与其相关的引用环境组合而成的一个实体,可以在变量的作用域外继续调用这个变量;如下所示:

package main
​
import "fmt"func main() {
    fuc := f(10)
    fmt.Println(fuc(10))
}
​
// 这个变量 i 的作用域在当前这个函数内,但是闭包会保存这个变量的值,到了真正调用这个闭包时使用,这就变相的延长了这个变量的作用域
func f(i int) func(j int) int {
    //此次返回的这个函数就是闭包
    return func(j int) int {
        return i + j
    }
}

web框架beego的中间件中也是使用到了闭包来实现中间件的调用,下面是一个具体的例子

type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}
​
// 定义了一个函数类型
type HandlerFunc func(resp http.ResponseWriter,req *http.Request)
​
// 该函数类型实现了 Handler 接口,所以就可以将跟它签名相同的方法也转化为 handler
func (h HandlerFunc) ServeHTTP(resp http.ResponseWriter,req *http.Request){
    h(resp,req)
}
​
// Middleware middleware which add X-Request-ID header in the http request when not exist
func Middleware(skippers ...middleware.Skipper) func(http.Handler) http.Handler {
    return middleware.New(func(w http.ResponseWriter, r *http.Request, next http.Handler) {
        rid := r.Header.Get(HeaderXRequestID)
        if rid == "" {
            rid = uuid.New().String()
            r.Header.Set(HeaderXRequestID, rid)
        }
        w.Header().Set(HeaderXRequestID, rid)
        //此处就是调用下一个 handler 的 ServeHTTP 方法
        next.ServeHTTP(w, r)
    }, skippers...)
}
​
// New make a middleware from fn which type is func(w http.ResponseWriter, r *http.Request, next http.Handler)
func New(fn func(http.ResponseWriter, *http.Request, http.Handler), skippers ...Skipper) func(http.Handler) http.Handler {
    return func(next http.Handler) http.Handler {
       // 此处将这个闭包转化为 HandlerFunc ,则这个闭包也实现了 Handler 接口,而其实现体就是这个闭包本身
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            for _, skipper := range skippers {
                if skipper(r) {
                    next.ServeHTTP(w, r)
                    return
                }
            }
​
            fn(w, r, next)
        })
    }
}
​
for i := len(mws) - 1; i >= 0; i-- {
        if mws[i] == nil {
            continue
        }
      // 此处就相当于一个链子一样,将所有的闭包函数链接起来,但是我感觉更像是闭包栈,比如,现在有三个闭包 A,B,C 首先进栈的是 C,然后将 C 作为参数传递给 B,然后再把 B 作为参数,将其传递给 A
      // 在 A 的时候执行 A.ServeHTTP() 方法,此处除了执行 A 这个闭包本身的逻辑之外,还会再调用 next.ServeHTTP 而这个 next 就是作为参数传递给 A 的 B,依次类推
        app.Server.Handler = mws[i](app.Server.Handler)
}
总结

现在发现很多很多设计模式中的类,都可以使用匿名函数进行代替