前言
新的項目終于準備要真正開始使用Lua進行開發(fā)了滥比,所以筆者最近也開始了對Lua的學習盗扇,從以前的強類型的靜態(tài)語言C#,到動態(tài)腳本語言寥假,多少會有一點點不習慣市框。除此之外,思考方式也會有細微的差異糕韧,畢竟Lua里并沒有類這個概念枫振。所以在接下來的幾篇文章中喻圃,我會嘗試將Lua是如何模擬類這一過程梳理清楚,畢竟是剛剛開始粪滤,很多感受比較直觀以及浮于表面斧拍,如果有什么錯誤疏漏之處,也希望各位能夠指出~
1.Lua中的table
如果說在python中一切皆對象的話杖小,那么Lua可以說是一切皆table了肆汹,table是是Lua中極其強大的數(shù)據(jù)結(jié)構(gòu),一個模塊予权,一個數(shù)組昂勉,一個字典,他們都可以用table實現(xiàn)扫腺,我們模擬面向?qū)ο缶幊谈谡眨瑃able也是必不可少的,我們的每一個對象其實也就是一個table笆环。關(guān)于luatable的介紹網(wǎng)上有非常多攒至,這里就不展開細說了~
2.元表
當我第一次接觸到Lua元表的概念時,我腦海里就一個想法“原來表還能夠這么玩”咧织。為什么需要出現(xiàn)元表這個概念,因為Lua的表實在是太萬能了籍救,對單一表操作再某些場景下很難滿足我們的需求习绢,所以元表的出現(xiàn),可以讓我們高度的自定義兩個表之間的操作蝙昙。
2.1設(shè)置元表
設(shè)置元表非常的簡單闪萄,如下代碼所示,在下文中奇颠,我將setmetatable的第一個參數(shù)所表示的表稱為普通表
败去,第二個參數(shù)所表示的表為元表
mytable = {}--普通表
mymetatable = {}--元表
setmetatable(mytable , mymetatable)--將mymetatable設(shè)置為mytable的元表
這一段代碼等價于
mymetatable = {}
mytable = setmetatable({},mymetatable)
setmetatable()為Lua的提供的內(nèi)置方法,其返回值是普通表的引用烈拒。
3.元方法
在設(shè)置了元表后圆裕,我們需要怎么操作才能發(fā)揮元表的作用呢,這就不得不提到元方法了荆几。我們首先介紹兩個使用頻率最高的元方法__index
以及__newindex
吓妆。我們使用一種數(shù)據(jù)結(jié)構(gòu)來存儲數(shù)據(jù),使用時無外乎兩種基本操作(元其實就是基本的意思)吨铸,讀和寫行拢,對于table來說也就是索引操作
和賦值操作
3.1__index
如果我們想自定義對一張普通表索引時的一些特殊行為,我們可以通過為其元表添加__index
這個key來實現(xiàn)自定義(注意诞吱,是在元表中添加__index
而不是直接設(shè)置__index
)舟奠。設(shè)置的方式非常簡單竭缝,我們繼續(xù)使用2.1中的mytable
作為例子。
3.1.1指向table的情況
mymetatable = { __ index = { key2 = "value2" }}
mytable = setmetatable({},mymetatable)
--等價于以下代碼沼瘫,所以希望大家以后看到網(wǎng)上各種寫法時不再感到疑惑
mytable = setmetatable({},{ __ index = { key2 = "value2" })
上述代碼就在mymetatable
這個表中設(shè)置了__index
這個key抬纸,而這個key指向的是一個table。
在
__index
指向的是一個table的情況下晕鹊,對普通表進行索引操作時
- 若普通表存在該key則返回該key所指向的value
- 若普通表不存在該key松却,則嘗試查找該普通表的元表,如果元表中沒有
__index
則返回nil溅话,如果有則繼續(xù)對__index
所指向的表進行索引晓锻。- 若該key存在,則返回該key所指向的value
- 若該key不存在飞几,則返回nil
舉個栗子砚哆,我們依然用上面的代碼,只不過在普通表中增加一個key
mymetatable = { __ index = { key2 = "value2" }}
mytable = setmetatable({ key1 = "value1" },mymetatable)
--普通表存在key1屑墨,所以返回value1
print(mytable.key1) --輸出value1
--普通表不存在key2躁锁,__index指向的table中存在,所以返回value2
print(mytable.key2) --輸出value2
--普通表和元表都不存在key3卵史,所以返回nil
print(mytable.key3) --輸出nil
3.1.2指向function的情況
mytable = setmetatable({}, { __index = function()
print("你正在嘗試索引mytable中沒有的key")
end}
)
returnValue = mytable.key1 --輸出 "你正在嘗試索引mytable中沒有的key"
print(returnValue) --輸出 nil
__index
還可以指向一個function
當我們試圖索引一個普通表中不存在的key時战转,如果元表中存在__index,且__index指向的是一個function以躯,那么就會去執(zhí)行這個function槐秧,索引所得到的值則是該function的返回值(上面例子代碼無返回值所以輸出為nil)
--存在返回值的情況
mytable = setmetatable({}, { __index = function()
print("你正在嘗試索引mytable中沒有的key")
return 58
end}
)
returnValue = mytable.key1 --輸出 "你正在嘗試索引mytable中沒有的key"
print(returnValue) --輸出 58
3.2__newindex
與索引相對應的,如果我們想自定義對一個普通表的賦值操作忧设,就可以使用__newindex
元方法刁标。其使用邏輯與__index
基本相似,一樣是存在指向table址晕,或者指向function的區(qū)別膀懈。
3.2.1指向table
--指向table
mymetatable = { key2 = "value2" }
mytable = setmetatable({key1="value1"}, { __newindex =mymetatable})
print(mytable.key1) --輸出 value1
mytable.key1="modified1"
print(mytable.key1) --輸出modified1
mytable.key2="modified2"
print(mytable.key2) --輸出nil
我們首先嘗試獲取mytable
中的key1,然后對其進行賦值谨垃,因為mytale
中存在key1启搂,所以直接賦值即可,我們通過前后的print
可以驗證這一點刘陶。如果我們嘗試對一個mytable
中沒有的key進行賦值狐血,那會怎么樣呢
首先會查詢該普通表的元表中是否有__newindex這個key
- 若沒有,則直接在普通表中增加這個key易核,對其進行賦值操作
- 若存在__newindex匈织,且其指向的是一個table,那么就會在__newindex指向的table中增加這個key,并且進行賦值
有一個非常容易引起迷惑的點我們需要注意的是
__index與__newindex的操作是完全獨立的!
所以一開始可能會有些疑惑缀匕,為什么上面代碼輸出會是nil纳决,我們不是明明已經(jīng)設(shè)置了key2的值了嗎?我們確實是成功進行了賦值操作乡小,但是根據(jù)上面的邏輯阔加,因為mytable
中不存在key2,所以我們實際上是對__newindex所指向的mymetatable
進行了賦值操作满钟。實際上普通表是沒有任何變化的胜榔,所以我們最后一行代碼,嘗試索引mytable
的key2湃番,按照3.1.1中的邏輯夭织,因為沒有設(shè)置__index
方法,所以返回的會是nil吠撮。如果我們希望訪問到key2尊惰,下面的代碼就可以做到了
print(mymetatable.key2) --輸出 modified2
3.2.2指向function
--指向function
mytable = setmetatable({key1="value1"}, { __newindex = function()
print("該表禁止增加新的key!")
end})
mytable.key2 = "value2" --輸出 "該表禁止增加新的key泥兰!"
如果__newindex
指向的是function弄屡,那么在對一個普通表中不存在的key進行賦值時,會執(zhí)行這個function鞋诗。
3.2.3function帶有參數(shù)的情況
在3.1.2以及3.2.2中的示例只是最基礎(chǔ)不帶參數(shù)的情況膀捷,其實在__index
和__newindex
這兩個元方法指向function類型時,是會傳遞參數(shù)進來的削彬,直接上代碼全庸。
__index
默認傳遞的兩個參數(shù)是調(diào)用該方法的table
,以及嘗試訪問的key
mytable = setmetatable({}, { __index = function(t,k)
print("你正在嘗試索引mytable中沒有的key")
print(t,k)--輸出 table: 0x5621b1f07270 key1
end}
)
returnValue = mytable.key1 --輸出 "你正在嘗試索引mytable中沒有的key"
print(returnValue) --輸出 nil
而__newindex
默認傳遞進來的是三個參數(shù)吃警,調(diào)用該方法的table
糕篇,需要賦值的key
以及想要賦予的value
mytable = setmetatable({key1="value1"}, { __newindex = function(t,k,v)
print("該表禁止增加新的key啄育!")
print(t,k,v)--輸出table: 0x55a8f8aefbb0 key2 value2
end})
mytable.key2 = "value2" --輸出 "該表禁止增加新的key酌心!"
其他的元方法
除了__index
,__newindex
這兩個元方法外挑豌,還有很多別十分有用的元方法安券,等我后面用到的時候會陸續(xù)添加進來!
最后
理解了元表以及元方法后氓英,在下一章中侯勉,我們就可以開始在Lua中真正的去模擬面向?qū)ο笏枷胫校庋b铝阐,多態(tài)址貌,繼承的特性了!