前言
在非常非常詳細的Lua面向對象(一)——元表與元方法中說完了一些基礎概念,這些是我們使用Lua模擬面向對象的基礎,但是在真正的實際開發中我們的代碼肯定是不能像上一篇那樣寫的,這樣可讀性太差了,而且冗余的代碼非常多,每次
setmetatable
都看起來很長很麻煩,所以為了本篇介紹的一些語法會讓我們的代碼更加貼近實際開發的習慣。在這里我有一個建議!就是我們剛開始接觸lua中的self
的時候,先不要從更高更次的抽象去理解(什么這樣代表靜態方法呀,那樣代表實例方法呀,什么指向調用者自身啊之類的),我們還是返璞歸真,從table
的角度去理解,相信很快就能理解了!
1.更簡潔的設置元表
首先先簡化上一篇文章中的setmetatable
的寫法,在上一篇文章中,我們為普通表聲明一個帶有__index
元方法的元表是這樣寫的
_M= { __ index = { key2 = "value2" }}
mytable = setmetatable({},_M)
emmmm,這樣寫雖然好理解,但是這樣其實是多出了一個由__index
指向的匿名表。所以我們修改一下寫法
_M= { key2 = "value2" }
mytable = setmetatable({},_M)
_M.__index=_M
看官們細品一下這兩種寫法的異同,當我們嘗試索引一個mytable普通表中沒有的key
的時候,就去去索引元表的__index
,發現__index
指向的是一個table
_M,所以就會繼續在_M中索引。
眾所周知,Lua的table
中的value
值是可以指向一個function
的,那現我們將對_M的賦值放到一個初始化方法中,這樣在我們需要用到這個表之前先調用一下這個初始化方法然后再使用,我們暫且把這個初始化方法起名叫New
吧。所以代碼如下:
_M={}
mytable = setmetatable({},_M)
function _M.New()
_M.name='my name is _M.'
_M.age=22
_M.__index=_M
end
然后我們只需要在打算訪問mytable
之前,先調用一次_M.New()
完成對表中一些值的初始化,然后就可訪問了,嗯是不是有點我們新建對象內味了。
_M.New()
print(mytable.name) --輸出my name is _M.
print(mytable.age) --輸出22
OK,打住,我們先繼續講一講點號和冒號還有self
2.最普通的點號調用
就像上面例子中的New()
方法,我們最簡單自然調用它的辦法就是_M.New()
,在一般情況下這樣調用一個方法已經能達到我們的目的了,比如我在一個table
中添加一個很普通的輸出方法,就這樣直接調用這個tableA.Print()
就能達到我們輸出的效果了。
tableA = {}
function tableA.Print()
print('output from tableA')
end
tableA.Print()
但是如果我往tableA
中添加了幾個屬性,然后我希望在PrintA
中去訪問一下這幾個屬性,那我們需要怎么辦呢,也很簡單,就直接在方法里面顯式的聲明就好了嘛!
tableA = {}
--為這個table添加一個屬性
tableA.keyA='valueA'
function tableA.Print()
print(tableA.keyA) --輸出valueA
end
tableA.Print()
這看起來并沒有self
和冒號什么事對嗎。那我們現在改一改上面的代碼,我新建一個表tableB
然后把tableA
設置為其元表。然后用tableB
去調用Print()
方法(不知道為什么能調用成功的同學可以去上一節看看就知道了~),依舊可以輸出valueA
tableA = {}
--為這個table添加一個屬性
tableA.keyA='valueA'
tableA.__index=tableA
function tableA.Print()
print(tableA.keyA)
end
--將tableA設置為tableB元表,然后調用一下Print()
tableB=setmetatable({},tableA)
tableB.Print()--輸出valueA
但是現在我想在tableB
中也添加一個keyA
(沒想到吧!),然后這次我想讓Print()
打印出tableB
中'keyA'的值了,這時候我們的點號就束手無策了。
tableA = {}
--為這個table添加一個屬性
tableA.keyA='valueA'
tableA.__index=tableA
function tableA.Print()
print(tableA.keyA)
end
--將tableA設置為tableB元表,然后調用一下Print()
tableB=setmetatable({ keyA = 'valueB' },tableA)
tableB.Print()--輸出valueA
3.self和冒號來了
想要實現我們上面的需求,self關鍵字就可以輕而易舉的達到了!我們稍微改變一下Print()
的代碼。大家可以看到,我只是簡單地加入了一個名為self
的參數,并且把對tableA.keyA
的訪問,換成了對self.keyA
的訪問。
--修改前的代碼
function tableA.Print()
print(tableA.keyA)
end
tableB=setmetatable({ keyA = 'valueB' },tableA)
tableB.Print()--輸出valueA
--修改后的代碼
function tableA.Print(self)
print(self.keyA)
end
tableB=setmetatable({ keyA = 'valueB' },tableA)
tableB.Print(tableB)--輸出valueB
經過這樣修改后我們再通過tableB.Print(tableB)
,就能夠輸出tableB.keyA
的值了。至此,self
的作用已經很明確了,
self
代表的就是作為參數傳入的那個table
當我們清楚了這點后,對于冒號調用的理解也就水到渠成了,對于上面tableB.Print(tableB)
這樣的寫法,如果我們想省略掉參數里的tableB
,只需要改為tableB:Print()
這樣就可以了
冒號調用,代表默認將調用者作為self參數,所以我們可以不用顯式的將調用者作為參數傳遞了
最后再多嘴說一種情況,加深一下對self
的理解。如果現在我仍然想通過tableB
來調用Print()
,但是,我這次不想輸出tableB
的keyA
了,我想輸出tableA
的'keyA'了嘿嘿(這嘴臉有沒有讓你們想起自己的PM或者策劃),這也好辦,我們只需要將tableA
作為self參數傳入就可以了,