本站文章均為Jensen抹茶喵原創,轉載務必在明顯處注明:
轉載自【博客園】 原文鏈接:http://www.cnblogs.com/JensenCat/p/5112420.html
1.什么是閉包
支持閉包特性通常需要一個嵌套函數,通過執行嵌套函數來改變所在父函數的局部變量狀態,父函數保存調用上下文狀態,而嵌套函數負責修改狀態的改變.(簡單來說就是得支持函數嵌套)
下面就是一個Lua閉包:
function counter() local cnt = 0 --返回匿名函數也是可以的 local Closure = function() cnt=cnt+1 print("print:"..cnt) end return Closure end function main() local Closure = counter() Closure() --print:1 Closure() --print:2 Closure() --print:3 local newCounter = counter() newCounter() --print:1 newCounter() --print:2 Closure() --print:4 --可以看出他們引用的計數對象是不一樣的,更像是同一個類的2個不同的對象 end
2.閉包的作用
下面是一個java的類,getName方法獲取到了類對象的私有成員變量
class Person { private String name; public String getName() { return name; } }
通過上面的方式可以獲取到一個類內部的私有屬性,同樣的,在lua中可以通過某個方法來獲取這個方法的局部變量,然后通過這個方法內的方法來讀取想要的變量值。
function func3() local num3 = 44 function func4() return num3 end return func4 end local func = func3(); print(func())

解釋:
1.在外部無法獲取到func3內部的局部變量,但是func3內部的局部方法func4卻可以獲取到,因此返回一個func4的引用 ,這樣在外部通過這個func4就可以獲取到func3的內部變量。
2.雖然是繞了一個圈子,但是在方法外部卻通過這樣一個手段獲取到了內部的值。而這個方法內的局部方法func4就叫做閉包,按照很多書上的概念,這個方法搭建了方法內部與方法外部的橋梁,使得在外部也可以任意的獲取到方法內部的資源。
3.但是閉包會造成變量在內存中持久占用,因此會有一定的性能問題,最好不要輕易使用,即便使用也要在恰當的實際進行釋放。
3.游戲開發中的應用
--以下用cocos2dx中的Lua來舉例... --2dx通過tolua++把類方法導出 --舉例api --按鈕響應回調函數格式為: --luaFunc(event) --event為觸摸按下,觸摸移動,觸摸離開等事件 --lua中的API為: --UIButton::addListenHandler(luaFunc) --實際需求是我按鈕按下時,我需要改變按鈕自身的紋理...此時回調中卻沒有按鈕本身的對象(sender),怎么辦呢? --利用閉包就輕松解決了 --下面是LUA實戰例子:一個testUI的頁面類 local testUI = testUI or {} local testUI:onBtnClick(sender,event) --可獲取的參數有:隱藏的self,btn,event end function testUI:initButton() local btn = UIButton:create() --重點來了 btn:addListenHandler( function(event) --使用閉包把self,btn都傳進去了.... self:onBtnClick(btn,event) end ) end return testUI
4.lua函數遞歸以及尾調用消除
1)遞歸示例:
--反面遞歸例子(遞歸必須在初始化以后才能調用) local func = function(n) if n > 0 then return func(n - 1) --此處調用錯誤 end --正確例子1 local func func = function(n) if n > 0 then return func(n - 1) --此處調用錯誤 end --正確例子2(此處函數展開后解釋為例子1的代碼再執行) function func(n) if n > 0 then return func(n - 1) --此處調用錯誤 end --如果是兩個函數嵌套遞歸(超前遞歸,必須先聲明) local g local f --這里不能加local..不然等於聲明了多一個局部變量了,遞歸的對象就不對了 function g() f() end --這里不能加local..不然等於聲明了多一個局部變量了,遞歸的對象就不對了 function f() g() end
2)尾調用消除(遞歸的時候如果返回的函數是最后的執行...則不損耗棧空間,相當於GOTO語句)
--尾調用消除 function g() return a,b end --正確例子 function f() return g() --正確的尾調用消除 end --錯誤例子1 function f(x) return g() + 1 --最后執行的是加法 end --錯誤例子2 function f(x) return (g()) --最后執行的是強制返回1個值 end --錯誤例子3 function f(x) return x or g() end
總結:由於LUA尾遞歸調用這個性質,我們可以用GOTO來實現狀態機了
