函数是一种“第一类值”,它们具有特定的词法域。
“第一类值”表示在lua中函数域其他传统类型的值句用相同的权利。函数可以存储到变量中(无论是全局变量还是局部变量)或table中,可以作为实参传递给其他函数,也可以作为其他函数的返回值。
“词法域”是值一个函数可以嵌套在另一个函数中,内部的函数可以访问外部函数中的变量。
函数与所有其他值一样都是匿名的,即他们都没有名称。当讨论一个函数名,实际上是在讨论一个持有某函数的变量。这与其他变量持有各种值一个道理,可以以多种方式来操作这些变量。如下例:
local a = {p = print} a.p("hello world") print = math.sin a.p(print(1)) sin = a.p sin(10,20)
输出为:
hello world 0.8414709848079 10 20
我们经常这样定义函数
function foo(x) return 2 * x end
实际上,这只是一种“语法糖”而已;上述代码只是下面代码的一种简化书写形式
foo = function (x) return 2 * x end
实际上,一个函数定义实际就是一条语句(更准确地说是一条赋值语句),这条语句创建了一种类型为“函数”的值,并将这个值赋予一个变量。由于函数在Lua中就是一个普通的值,所以不仅可以将其存储在全局变量中,还可以存储在局部变量甚至table的字段中。
关于函数的语法域,我们看一下下面的例子:
function new() local i = 0 return function() i = i+1 return i end end local new1 = new() print(new1()) print(new1())
输出为:
1 2
在上述代码中,有一个变量i,对于函数new来说,i是一个局部变量,但是对于匿名函数来说,当它访问这个i时,i既不是全局变量,也不是局部变量,对于我们来说,我们称这样的变量为一个“非局部的变量”。下面这段代码也是同样的道理:
function new(i) return function() i = i+1 return i end end local new1 = new(10) print(new1()) print(new1())
输出:
11 12
匿名函数访问了一个“非局部的变量”i,该变量用于保持一个计数器。乍一看,由于创建变量i的函数,也就是new已经返回,所以之后每次调用匿名函数时,i都应该是已经超出了作用范围。但是,Lua会以closure的概念来正确地处理这种情况。一个closure就是一个函数加上该函数所需访问的所有“非局部的变量”。如果再次调用newCounter,那么它会创建一个新的局部变量i,从而将得到一个新的closure。关于闭包,我们接下来会进行讨论。