golang可以跨平台吗(Golang闭包)
golang可以跨平台吗(Golang闭包)package main import "fmt" func main() { var functor func() int functor = func() int { return 10 } fmt.Println(functor()) functor = func() int { return 15 } fmt.Println(functor()) functor = func() int { return 20 } fmt.Println(functor()) }运行结果:➜ closure git:(mas
预计阅读时间:25分钟

南京夫子庙
闭包是高级语言通常都会包含的一种语法,Python和Go中都支持这个语法。
那么,究竟什么是闭包呢?
首先我们从匿名函数开始讲起:
匿名函数同样Python和Go都支持匿名函数,以Go为例,我们看一个简单的例子吧:
package main
import "fmt"
func main() {
        lambda := func() {
                fmt.Println("I'm an anonymous function")
        }
        lambda()
}
    
运行结果:
➜  closure git:(master) ✗ go run lambda.go 
I'm an anonymous function
    
函数作为一等公民(First class citizen),可以把它赋给一个变量,这样就可以做到实时更新:
package main
import "fmt"
func main() {
        var functor func() int
        functor = func() int {
                return 10
        }
        fmt.Println(functor())
        functor = func() int {
                return 15
        }
        fmt.Println(functor())
        functor = func() int {
                return 20
        }
        fmt.Println(functor())
}
    
运行结果:
➜  closure git:(master) ✗ go run functor_realtime_update.go
10
15
20闭包
    
如果一个内部嵌套函数中,引用了外部定义的变量,那么我们就称这个函数为“闭包”,同时我们称被引用的变量为“引用变量”或“自由变量”。
下面我们看一个引用外部变量的嵌套函数:
package main
import "fmt"
func main() {
        var nestedFunctorInMain func()
        counter := 0
        nestedFunctorInMain = func() {
                counter  = 1
                fmt.Println(counter)
        }
        nestedFunctorInMain()
        nestedFunctorInMain()
        nestedFunctorInMain()
}
    
运行结果:
➜  closure git:(master) ✗ go run nested_function.go 
1
2
3
    
这里的“nestedFunctorInMain”就是一个闭包,它嵌套在入口函数“main”中,并且引用了外部变量“counter”。
那闭包有什么特殊的用处呢?最简单的,利用闭包特性,我们可以达到数据隔离的目的:
package main
import "fmt"
func main() {
  counter := newCounter()
  fmt.Println(counter())
  fmt.Println(counter())
}
func newCounter() func() int {
  n := 0
  return func() int {
    n  = 1
    return n
  }
}
    
运行结果:
➜  closure git:(master) ✗ go run data_isolation.go 
1
2
    
之前的嵌套函数的例子里面,我们的闭包使用了main函数中的外部变量,但是这个变量使用范围过大,其他函数也可以更新它,在本例中,我们设置在新函数中设置变量“n” 而嵌套匿名函数引用它,即使“newCounter”函数执行完毕,变量依然存在闭包中,且外部无法访问,这样就很方便地达到了数据隔离的目的。
闭包中遇到的常见问题- defer和go使用函数调用作为参数
 
假设我们有个setup和teardown的功能需要实现,即在函数执行前利用setup做一些初始化操作,然后在执行结束后,做环境清理工作:
package main
import "fmt"
func main() {
  defer func() {
    fmt.Prinltn("teardown")
  }
}
    
运行结果:
➜  closure git:(master) ✗ go run defer_need_func_call.go 
# command-line-arguments
./defer_need_func_call.go:3:8: imported and not used: "fmt"
./defer_need_func_call.go:8:4: expression in defer must be function call
    
因为“defer”和“go”关键字的参数是函数调用,而不是函数,所以按照本例的写法,编译代码时会报错:“expression in defer must be function call”。
我们改进一下:
package main
import "fmt"
func main() {
        defer setup()
}
func setup() func() {
  fmt.Println("pretend to set things up")
  return func() {
    fmt.Println("pretend to tear things down")
  }
}
    
运行结果:
➜  closure git:(master) ✗ go run defer_need_func_call.go 
pretend to set things up
    
“teardown”并未得到执行!为什么?
仔细看本例中代码,“setup”函数返回匿名函数,即“teardown”,但是“teardown”并未有机会得到显式执行,所以代码未运行到。
再改进一下:
package main
import "fmt"
func main() {
        defer setup()()
}
func setup() func() {
  fmt.Println("pretend to set things up")
  return func() {
    fmt.Println("pretend to tear things down")
  }
}
    
运行结果:
➜  closure git:(master) ✗ go run defer_need_func_call.go 
pretend to set things up
pretend to tear things down
    
显式执行返回的匿名函数,“teardown”部分得到执行。
- “for”循环中的变量是引用传递而非值传递
 
这个问题很容易让人迷惑,我们先看个例子:
package main
import "fmt"
func main() {
  var functions []func()
  for i := 0; i < 10; i   {
    functions = append(functions  func() {
      fmt.Println(i)
    })
  }
  for _  f := range functions {
    f()
  }
}
    
运行结果:
➜  closure git:(master) ✗ go run for_loop_refer_assign.go 
10
10
10
10
10
10
10
10
10
10
    
按照我们在理解,不是应该按顺序输出“0”到“9”的数字吗?这是因为,当我们在“for”循环做迭代的时候,“for”内部的变量传递不是我们通常理解的值传递,而是引用传递,导致最后函数切片中所有的函数中使用到的变量“i”都是循环之后变量“i”的值,从而最后输出的都是“10”。
那如果我们想按照初衷一次打印每个迭代中的数字,如何做呢?
有一种办法可以做到,因为函数参数是值传递,可以通过一个辅助函数达到目的:
package main
import "fmt"
func main() {
  var functions []func()
  for i := 0; i < 10; i   {
    functions = append(functions  build(i))
  }
  for _  f := range functions {
    f()
  }
}
func build(val int) func() {
  return func() {
    fmt.Println(val)
  }
}
    
运行结果:
➜  closure git:(master) ✗ go run for_loop_refer_assign.go 
0
1
2
3
4
5
6
7
8
9          




