通常 Go 语言中的命名函数只能在包级别定义,但是为了方便广大 Gopher,Go 语言允许 function literal 在任何表达式中表示一个函数。
那么 function literal 是什么呢?它就是没有名字的函数体。这些匿名函数与 JavaScript 领域内的函数是一样的。
这些函数可以访问整个作用域,所以最内层的函数可以访问包裹它的函数中的变量。
从截屏中可以看到 appendArray() 是一个返回值类型为 func() []int 的函数,一个 appendArray() 调用将创建一个本地变量 x 与 y 并返回匿名函数,每次执行它都将 x 加 1,然后将 x 追加到 slice y 中。第二个 appendArray() 调用创建了第二个本地变量 x 与 y ,它自增这个本地变量并且返回指定函数。
这与 Go 的预言正好相符:函数是引用类型,函数的值是不可以进行比较的。
匿名函数是可以保存它们局部变量状态的,并且还可以访问或更改包裹它的函数的局部变量。
保存变量状态这个属性有助于我们解决很多计算机科学中的问题,例如:拓扑排序一下一个全栈开发工程师的技能树
//topological sort
package main
import (
"fmt"
"sort"
)
var pathOfFullstack = map[string][]string{
"fullstack developer": {"C#", "Python", "Go", "Angular", "React"},
"C#": {"C"},
"Angular": {"html", "Css", "Javascript"},
"Go": {"C#", "Python"},
"React": {"html", "Javascript", "JSX"},
}
func main() {
for i, path := range topoSort(pathOfFullstack) {
fmt.Printf("%d:\t%s\n", i+1, path)
}
}
func topoSort(m map[string][]string) []string {
var pathOrder []string
visited := make(map[string]bool)
var visitAll func(items []string)
visitAll = func(items []string) {
for _, item := range items {
if !visited[item] {
visited[item] = true
visitAll(m[item])
pathOrder = append(pathOrder, item)
}
}
}
var paths []string
for devPath := range m {
paths = append(paths, devPath)
}
sort.Strings(paths)
visitAll(paths)
return pathOrder
}
// Output
/*
1: html
2: Css
3: Javascript
4: Angular
5: C
6: C#
7: Python
8: Go
9: JSX
10: React
11: fullstack developer
*/
所以下一次你定义 API,就可以施展一下你自己的 Go 语言闭包了。