前提概念
词法定界:当一个函数内嵌套另一个函数的时,内嵌函数可以访问外部函数的局部变量,这种特征叫做词法定界。
第一类值:在Lua中,函数是一个值,它可以存在于变量中、可以作为函数参数,也可以作为返回值return。
upvalue:内嵌函数可以访问外部函数已经创建的局部变量,这些局部变量则称为该内嵌函数的外部局部变量(即upvalue)。
Lua中upvalue存储结构:GC信息、指向原型(Prototype)的指针、若干指向upvalue指针
一个upvalue有两种状态:open和closed。
当upvalue创建时,是open状态,并且它的指针指向Lua栈中对应的变量。
当Lua关闭了一个upvalue,upvalue指向的值被复制到upvalue结构内部,并且指针也相应进行调整。如图所示:
定义
闭包(closure)是由一个函数和该函数会访问父函数的外部局部变量(upvalue)组成的实体,主要应用在嵌套函数和匿名函数里。
简单理解,闭包由三部分组成:外部函数+外部函数的upvalue+内部函数(闭包函数)
应用
1、闭包的数据隔离
不同实例上的两个不同闭包,闭包中的upvalue变量各自独立,从而实现数据隔离
function func()
-- 局部变量 index 保存在函数内部中
local index = 0
return function()
print("index is: ", index)
index = index + 1
end
end
-- Test
local f1 = func()
f1() -- index is: 0
f1() -- index is: 1
local f2 = func()
f2() -- index is: 0
f2() -- index is: 1
2、闭包的数据共享
两个闭包共享一份变量upvalue,闭包创建时需要的变量不在堆栈上,引用的是更外部函数的局部变量(即upvlaue),变量(对应于示例中的 变量n)是同一个,引用也指向同一个地方,从而实现对共享数据进行访问和修改。
function shareVar(n)
local function func1()
print(n)
end
local function func2()
n = n + 10
print(n)
end
return func1,func2
end
--创建闭包,f1,f2两个闭包共享同一份upvalue
local f1,f2 = shareVar(1024)
f1() -- 输出1024
f2() -- 输出1034
f1() -- 输出1034
f2() -- 输出1044
3、利用闭包实现简单的迭代器
迭代器只是一个生成器,本身不带循环。需要在循环里面去调用它。
--- 利用闭包实现iterator,iterator是一个工厂,每次调用都会产生一个新的闭包,该闭包内部包括了upvalue(t,i,n)
--- 因此每调用一次该函数都会产生闭包,那么该闭包就会根据记录上一次的状态,以及返回table中的下一个元素
function iterator(t)
local i = 0
local n = #t
return function()
i = i + 1
if i <= n then
return t[i]
end
end
end
testTable = {1,2,3,"a","b"}
-- while中使用迭代器
iter1 = iterator(testTable) --调用迭代器产生一个闭包
while true do
local element = iter1()
if nil == element then
break;
end
print(element)
end
-- for中使用迭代器
for element in iterator(testTable) do --- 这里的iterator()工厂函数只会被调用一次产生一个闭包函数,后面的每一次迭代都是用该闭包函数,而不是工厂函数
print(element)
end
4、创建安全的运行环境(沙盒)
如,通过使用闭包重定义函数 io.open 来限制一个程序能够访问的文件
do
local oldOpen = io.open
local accessOk = function(filename, mode)
--<权限访问检查>
end
io.open = function (filename, mode)
if accessOk(filename, mode) then
return oldOpen(filename, mode)
else
return nil, "access denied"
end
end
end
参考资料