1.沉默技能——拒絕__index和__newindex效果
雖然__index和__newindex是很好用的功能,但是,有時候我們又希望很純粹地去調用table或者給table賦值。
那,這時候怎麼辦?給table重新設置一個元表?不,這個做法很糟糕~
於是,體貼的Lua又給我們提供了這樣的調用方式,如下代碼:
- local smartMan = {
- name = "none",
- }
- local t1 = {
- hehe = 123;
- };
- local mt = {
- __index = smartMan,
- __newindex = function(t, k, v)
- print("別賦值!");
- end
- }
- setmetatable(t1, mt);
- print(rawget(t1, "name"));
- print(rawget(t1, "hehe"));
- rawset(t1, "name", "小偷");
- print(t1.name);
通過rawget函數可以忽略元表的__index功效,純粹地從t1中調用字段。
rawget的第一個參數是要調用的table,第二個參數是table的字段名。
因此,通過rawget調用t1的name字段,只能返回nil,而調用hehe字段,則能正確取得值。
同樣的是,rawset函數可以忽略元表的__newindex功效,純粹地給t1賦值。
來看看輸出結果:
[LUA-print] nil
[LUA-print] 123
[LUA-print] 小偷
獲取name字段,輸出nil;
獲取hehe字段,輸出123;
修改name字段後,輸出”小偷”
這就相當於t1並不存在__index和__newindex元方法了。
怎麼樣,這個沉默技能很有意思吧。
2.只讀的table
吶,假設你又繼續是一個主程,你寫了一個很牛的功能,然後作爲主程的你,每晚都要回家看電影。
所以你的功能不得不交給公司裏那些剛畢業不到30年的新人去維護,讓他們天天加班到晚上6點半。(小若:喂!6點半算加班嗎?)
然而,這麼牛的功能,可不能被這些新人隨便改壞了,所以,除了保護table的元表之外,你還希望保護table的字段。
你要確保這些新人不會去修改你table的字段值。
沒錯,這時候就可以使用__index和__newindex來實現了,如下代碼:
- local function readOnly(t)
- local newT = {};
- local mt = {
- __index = t,
- __newindex = function()
- error("別修改我!我是隻讀的!");
- end
- }
- setmetatable(newT, mt);
- return newT;
- end
- local days = readOnly({"星期一", "星期二", "星期日"});
- days[2] = "星期三哪去了啊?" ;
這可能有點難弄懂,先來看看輸出結果吧:
[LUA-print] LUA ERROR: [string “src/main.lua”]:130: [string “src/main.lua”]:76: 別修改我!我是隻讀的!
沒錯,通過readOnly產生的table,是無法進行賦值操作的。
那麼,原理呢?我們來一步步思考吧:
a.首先,readOnly會創建一個新的table,然後把我們傳進去的table作爲__index元方法。
b.元表裏還增加了__newindex,用來阻止不存在字段的賦值操作。
c.readOnly返回的table已經不是我們原來的table了,它是一個空的table,但是它被設置了一個新的元表。
d.開始對days執行賦值操作:days[2] = “星期三哪去了啊?” 。
e.days是一個空的table,所以它不存在這個字段,也因此,會調用__newindex元方法,賦值失敗。
f.如果只是調用days,不進行賦值,如:print(days[2]); 則能正常輸出字段值,因爲days的元表裏有__index元方法。雖然days中不存在2這個字段,但是可以通過__index找到這個字段。
總而言之,最終,days成爲了一個只可以讀取,不能進行賦值操作的table。