非常非常詳細的Lua面向對象(二)——冒號,點號以及self

前言

非常非常詳細的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(),但是,我這次不想輸出tableBkeyA了,我想輸出tableA的'keyA'了嘿嘿(這嘴臉有沒有讓你們想起自己的PM或者策劃),這也好辦,我們只需要將tableA作為self參數傳入就可以了,

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 0.lua的特性是什么? 1)lua是一種輕量級的腳本解釋語言,在游戲開發過程中多用于實現需要進行熱更新的邏輯 它...
    你猜卟透_faa8閱讀 1,790評論 0 0
  • 元表這個詞聽起來就覺得抽象,我開始接觸Lua的時候就是這種感覺。其實不要被表面嚇到。 元表就是,如果一個table...
    程序員喜歡自嘲閱讀 12,022評論 0 6
  • 第一篇 語言 第0章 序言 Lua僅讓你用少量的代碼解決關鍵問題。 Lua所提供的機制是C不擅長的:高級語言,動態...
    testfor閱讀 2,719評論 1 7
  • 1. 寫在前面 很多時候我們都需要借助一些腳本語言來為我們實現一些動態的配置,那么就會涉及到如何讓腳本語言跟原生語...
    杰嗒嗒的阿杰閱讀 3,450評論 9 31
  • 在lua原生語法特性中是不具備面向對象設計的特性。因此,要想在lua上像其他高級語言一樣使用面向對象的設計方法有以...
    杰嗒嗒的阿杰閱讀 11,797評論 3 20