Lua中的闭包学习笔记
之前介绍Lua的数据类型时,也提到过,Lua的函数是一种“第一类值(First-ClassValue)”。它可以:
存储在变量或table(例如模块和面向对象的实现)里
t={p=print} t.p("justatest!")
作为实参(也称其为“高阶函数(higher-orderfunction)”)传递给其他函数调用
t={2,3,1,5,4} table.sort(t,function(a,b)return(a>b)end)
作为其他函数的返回值
functionfun1(x)returnfun2(x)end
函数在Lua里“第一类值”的特性,使它成为一种灵活,极具弹性的数据类型,同时,也让它衍生出一些特殊的功能强大的语言机制:
闭包(closure)
Lua中的函数是带有词法作用域(lexicalscoping)的第一类值,也可以说是函数变量的作用域,即函数的变量是有一定的效用范围的,变量只能在一定范围内可见或访问到。
例如如下代码:
functioncount() localuv=0 localfunctionretfun() uv=uv+1 print(uv) end returnretfun end
上面函数retfun定义在函数count里,这里可以把函数retfun看作是函数count的内嵌(inner)函数,函数count视为函数retfun的外包(enclosing)函数。内嵌函数能访问外包函数已创建的所有局部变量,这种特征就是上面所说的词法作用域,而这些局部变量(例如上面的变量uv)则称为该内嵌函数的外部局部变量(externallocalvariable)或upvalue。
执行函数count:
c1=count() c1() --输出1 c1() --输出2
上面两次调用c1,会看到分别输出1和2。
对于一个函数count里的局部变量uv,当执行完"c1=count()"后,它的生命周期本该结束,但是因为它已成了内嵌函数retfun的外部局部变量upvalue,返回的内嵌函数retfun以upvalue的方式把uv的值保存起来,因此可以正确把值打印出来。
这种局部变量在函数返回后会继续存在,并且返回的函数可以正常调用那个局部变量,独立执行其逻辑操作的现象,在Lua里称之为闭包(closure)
之所以说闭包是一个独立存在的个体,这个可以再把函数count赋给一个变量,然后执行看输出效果:
c2=count() c2() -- 输出1
c1跟c2都是相同的函数体,不过输出的值却不一样!这主要还是因为闭包是由相应函数原型的引用和外部局部变量upvalue组成。当调用函数造成upvalue值被改变时,这只会改变对应闭包的upvalue值,不会影响到其他闭包里的upvalue值,所以c1被调用2次后,外部局部变量uv的值的是2,而新创建的c2初始的外部局部变量uv是0,被调用之后会是1。