【轉載】Lua中實現類的原理

原文地址 http://wuzhiwei.net/lua_make_class/函數

不錯,將metatable講的很透徹,我終於懂了。lua

------------------------------------------------------------spa

 

Lua中沒有的概念,但咱們能夠利用Lua自己的語言特性來實現.net

下文將詳細的解釋在Lua中實現類的原理,涉及到的細節點將拆分出來說,相信對Lua中實現類的理解有困難的同窗將會釋疑。3d

類是什麼?

想要實現類,就要知道類究竟是什麼。code

在我看來,類,就是一個本身定義的變量類型。它約定了一些它的屬性和方法,是屬性和方法的一個集合。blog

全部的方法都須要一個名字,即便是匿名函數實際上也有個名字。這就造成了方法名和方法函數的鍵值映射關係,即方法名爲鍵,映射的值爲方法函數。seo

好比說有一個類是人,人有一個說話的方法,那就至關於,人(Person)是一個類,說話(talk)是它的一個方法名,說話函數是它的實際說話所執行到的內容。內存

人也有一個屬性,好比性別,性別就是一個鍵(sex),性別的實際值就是這個鍵所對應的內容。get

理解了類其實是一個鍵值對的集合,咱們不難想到用Lua中自帶的表來實現類。

實例是什麼?

若是理解了類實際就是一個鍵值映射的表,那麼咱們再來理解實例是什麼。

實例就是具備類的屬性和方法的集合,也是一個表了。聽起來好像和類差很少?

類全局只有一個集合,至關於上帝,全局只有一塊內存;而實例就普通了,普天之下有那麼多人,你能夠叫A說一句話,A便執行了他的說話方法,可是不會影響B的說話。由於他們是實例,彼此分配着不一樣的內存。

說了那麼多廢話,其實實例就是由類建立出來的值,試着把類想象成類型而不是類。

兩個語法糖

試着建立一我的類 Person

以上代碼將Person初始化爲一個表,這個表擁有一個爲name的鍵,其默認值是"這我的很懶"

說成白話就是人類擁有一個叫名字的屬性。

那就再賦予人類一個說話的功能吧。

以上代碼在Person表中加入一個鍵值對,鍵爲talk,值爲一個函數。

好了,只要調用,Person.talk(Person, "你好"),將會打印出:這我的很懶說:你好

不過在寫程序時,你們都習慣把function放在前面,這就是函數的語法糖:

這與上面的函數定義是等價的,可是這麼寫你就很難看出來talk實際上是Person表中的一個鍵,其對應的值爲一個函數。

固然嘴巴都是長在本身身上的,說話只能本身說,不可能本身張嘴別人說話,因此每次都傳個self參數實在是有點不美觀,因而冒號語法糖上場。

咱們還能夠這麼定義人類的說話功能:

這與上面兩段代碼都是等價的,它的變化是少了self的參數,將點Person.talk改成了冒號Person:talk

可是函數體內,卻依然可使用self,在使用:代替.時,函數的參數列表的第一個參數再也不是words,Lua會自動將self作爲第一個參數。這個self參數表明的意思就是這個函數的實際調用者。

因此咱們調用Person:talk("你好")Person.talk(Person, "你好")是等價的,這就是冒號語法糖帶來的便利。

如何查找表中的元素?

下面咱們須要理解在Lua的表中是怎麼查找一個鍵所對應的值的。

假設咱們要在表p中查找talk這個鍵所對應的值,請看下面的流程圖:

理解以上內容是本文的重點,反覆閱讀直至你記住了。

能夠看到,因爲metatable__index這兩個神奇的東西,Lua能在當前表中不存在這個鍵的時候找到其返回值。

下面將會講一講metatable這個語言特性。

對metatable的理解

metatable是什麼?

metatable的中文名叫作元表。它不是一個單獨的類型,元表其實就是一個表。

咱們知道在Lua中表的操做是有限的,例如表不能直接相加,不能進行比較操做等等。

元表的做用就是增長和改變表的既定操做。只有設置過元表的表,纔會受到元表的影響而改變自身的行爲。

經過全局方法setmetatable(t, m),會將表t的元表設置爲表m。經過另外一個全局方法getmetatable(t)則會返回它的元表m

注意:全部的表均可以設置元表,然而新建立的空表若是不設置,是沒有元表的。

元方法

元表做爲一個表,能夠擁有任意類型的鍵值對,其真正對被設置的表的影響是Lua規定的元方法鍵值對。

這些鍵值對就是Lua所規定的鍵,好比前面說到的__index__add__concat等等。這些鍵名都是以雙斜槓__爲前綴。其對應的值則爲一個函數,被稱爲元方法(metamethod),這些元方法定義了你想對錶自定義的操做。

例如:前面所說的__index鍵,在Lua中它所對應的元方法執行的時機是當查找不存在於表中的鍵時應該作的操做。考慮如下代碼:

pos表中本沒有z這個鍵,經過設置pos的元表爲m,並設置m__index對應的方法,這樣全部取不到的鍵都會返回「undefined」了。

以上咱們瞭解到,元表的__index屬性其實是給表配備了找不到鍵時的行爲。

注意:元表的__index屬性對應的也能夠爲一個表。

再舉個栗子,但願可以加深對元表和元方法的理解,__add鍵,考慮如下代碼:

表自己是不能用+連起來計算的,可是經過定義元表的__add的方法,並setmetatable到但願有此操做的表上去,那些表便能進行加法操做了。

由於元表的__add屬性是給表定義了使用+號時的行爲。

類的實現手段

好,假設前面的內容你都沒有疑問的閱讀完畢話,咱們開始進入正題。

請先獨立思考一會,咱們該怎麼去實現一個Lua的類?

思考ing…

種種鋪墊後,咱們的類是一個表,它定義了各類屬性和方法。咱們的實例也是一個表,而後咱們類做爲一個元表設置到實例上,並設置類的__index值爲自身。

例如人類:

爲了方便,咱們給人類一個建立函數create:

這樣咱們能夠很方便用Person類建立出pa和pb兩個實例,這兩個實例都具有Person的屬性和方法。

-----------------------------好久之後加的評論:(class是cocos2dx的framework裏面提供的一個方法,在functions.lua裏面,直接傳入子類和父類便可。)-------------

這篇文章很是有助於對metatable的理解。可是,我我的以爲,實現類,用metatable顯的太複雜,lua中直接用class實現更清晰。

好比 我定義一個類Person

local Person = class("Person")
function Person:ctor()
    self.name = "這我的很懶"
end
function Person:talk( words)
    print(self.name.."說:"..words)
end
return Person

定義好之後,這樣調用

import("..module.Person") -- 首先要引入Person類,看你的路徑修改

local
pa= Person.new() pa.name = "張三" pa:talk("路人甲") --張三說:我是路人甲
local pb= Person.new() 
pb.name
= "李四"
pb:talk(
"路人乙") --李四說:我是路人甲

我以爲這樣比metatable更加清晰明瞭,你以爲呢。

相關文章
相關標籤/搜索