闭包(closure)是由一个函数和该函数会访问到的非局部变量(upvalue)组成,其中非局部变量是指不在局部作用范围内定义的一个变量,但同时又不是一个全局变量,主要应用在嵌套函数和匿名函数中。我们看下面的例子:

function new()

local i = 0

return function()

i = i + 1

return i

end

end

local new1 = new()

print(new1())

print(new1())

输出:

1

2

上面的代码中,new函数返回了一个函数,而这个返回的匿名函数就是闭包组合部分的函数,而i则是非局部变量。在看一下下面的例子:

function test(num)

local function output()

print(num)

end

return output

end

local fun1 = test(10086)

fun1()

local fun2 = test(10000)

fun2()

这里fun1以及fun2的函数体时相同的,都是output的函数体,但是输出不同。因为fun1和fun2实际上是两个闭包,他们都拥有局部变量num的独立实例。事实上,lua编译一个函数时,会为它生成一个原型,其中包括了函数体对应的虚拟机指令、函数用到的常量值和一些调试信息。在运行的时候,每当lua执行一个函数的时候,他会创建一个新的数据对象,其中包含了相应函数原型的引用以及一个由所有upvalue引用组成的数组,而这个数据对象就是闭包。由此可见,函数是编译期的概念,而闭包时运行期的概念。

非局部变量实际上指的是变量而不是值,这些变量可以在内部函数之间共享,即upvalue提供一种闭包之间共享数据的方法。看下面的例子:

function share(n)

local function fun1()

print(n)

end

local function fun2()

n = n + 1

end

return fun1,fun2

end

local f1,f2 = share(10086)

f1()

f2()

f1()

f2()

f1()

输出:

10086

10087

10088

f1,f2都是闭包,他们共享了一个非局部变量n,当lua发现两个闭包的非局部变量指向的是当前堆栈上的相同变量时,只生成一个拷贝,然后让这两个闭包共享该拷贝,这样任意一个闭包对该非局部变量就行修改都会影响到其他闭包。

闭包在创建的时候他的非局部变量就已经不在堆栈上的情况也是有的,这是因为内嵌函数能引用更外层外包函数的局部变量,如下例:

function test(n)

local function fun()

local function inner1()

print(n)

end

local function inner2()

n = n + 1

end

return inner1,inner2

end

return fun

end

local t = test(10086)

local f1,f2 = t()

f1()

f2()

f1()

local g1,g2 = t()

g1()

g2()

g1()

f1()

输出:

10086

10087

10087

10088

10088

从输出我们可以看出闭包f1,f2,g1,g2都共有同一个upvalue,这是因为在创建inner1,inner2这两个闭包被创建时堆栈上根本未找到n的踪影,而是直接使用闭包fun的upvalue。t = test(10086)之后,t这个闭包一定已把n妥善保存好了,之后f1、f2如果在当前堆栈上未找到n就会自动到他们的外包闭包的upvalue引用数组中去找,并把找到的引用值拷贝到自己的upvalue引用数组中。所以f1、f2、g1和g2引用的upvalue实际也是同一个变量,而刚才描述的搜索机制则确保了最后他们的upvalue引用都会指向同一个地方。

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐