看一段javascript代码:
function a()
{
var i = 0;
function b()
{
alert(++i);
}
return b;
}
var c = a();
c();
这段代码有两个特点:
1,函数b嵌套在函数a内部;
2,函数a返回函数b。
当函数a的内部函数b被函数a外的一个变量c引用的时候(函数a中的变量i被函数b引用,函数b又被函数a外的变量c引用),就创建了一个闭包。简而言之,闭包的作用就是在a执行完并返回后,使得javascript的垃圾回收机制GC不会收回a所占用的资源,因为a的内部函数b的执行需要依赖a中的变量。
如果一个函数返回另一个函数,而被返回函数又需要外层函数的变量时,支持闭包机制的语言不会立即释放这个变量,而是允许被返回的函数引用这些变量,这个内部函数连同外层函数中的变量(对内层函数而言是自由变量)就形成了一个闭包。
闭包的定义
如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure)。闭包是可以包含自由(未绑定到特定对象)变量的代码块;这些变量不是在这个代码块内或者任何全局上下文中定义的,而是在定义代码块的环境中定义。
或者说,闭包是由函数和与其相关的引用环境组合而成的实体。闭包只是在形式和表现上像函数。函数是一些可执行的代码,这些代码在函数被定义后就确定了,不会在执行时发生变化,所以一个函数只有一个实例。闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。所谓引用环境是指在程序执行中的某个点所有处于活跃状态的约束所组成的集合。
闭包的应用场景
1,保护函数内的变量安全。代码中,函数a中变量i只有函数b才能访问,而无法通过其他途径访问到,因此保护了i的安全性。
2,在内存中维持一个变量。代码中,闭包的创建使得函数a中的变量i一直存在于内存中,因此每次执行c(),都会给i自加1。
支持闭包的语言一般要求:
1,函数是一阶值(First-class value),即函数可以作为另一个函数的返回值或参数,还可以作为一个变量的值;
2,函数可以嵌套定义,即在一个函数内部可以定义另一个函数;
3,可以捕获引用环境,并把引用环境和函数代码组成一个可调用的实体;
4,允许定义匿名函数;
Lua中的闭包示例
示例1:计数器
--[[
function makeCounter()
local count = 0
function incCount()
count = count + 1
return count
end
return incCount
end
--]]
-- 匿名的,与上面的功能一致
function makeCounter()
local count = 0
return function()
count = count + 1
return count
end
end
--]]
c1 = makeCounter()
c2 = makeCounter()
print(c1()) -- 输出1
print(c1()) -- 输出2
print(c2()) -- 输出1
print(c1()) -- 输出3
示例2:栈
function makeStack()
local data = {} -- 数据
local last = -1 -- 栈顶
local function push(e)
last = last + 1;
data[last] = e;
end
local function pop()
if last == -1 then
return nil
end
last = last - 1
return data[last + 1]
end
return function(index)
local tb = { push = push, pop = pop }
return tb[index]
end
end
s = makeStack()
s("push")("test0")
s("push")("test1")
s("push")("test2")
s("push")("test3")
print(s("pop")())
print(s("pop")())
print(s("pop")())