元表裏面有元方法
設置元表的方法:
setmetatable(tab, metatable
__index元方法
prototype = {x = 0, y = 0, width = 100, height = 100}
local metatable = {} -- 元表
metatable.__index = function(_, key) --元表裏面有__index元方法
return prototype[key]
end
-- 構造函數
function new(o)
setmetatable(o, metatable)
return o
end
w = new{x=10, y=20}
print(w.width)
w沒有width這個字段,但是有一個帶有__index
方法的元表,於是Lua會將w
和width
一起傳入__index
方法
當元方法是函數,Lua會以表和不存在的鍵作爲參數調用該函數。
當元方法是一個表,Lua會訪問這個表。也就是說,將4到6行替換成:
metatable.__index = prototype
可以得到相同的結果。
當元方法是一個函數,開銷會比表更昂貴,但是更靈活,例如可以通過函數實現多繼承、緩存等特性。
__newindex元方法
__index
用於表的查詢
__newindex
用於表的更新
對錶中不存在的鍵賦值,Lua會查找__newindex
元方法,如果該原方法是一個函數,則調用它,不賦值。如果該方法是一個表,則在此表賦值,而不是在原始的表中賦值。
組合使用這兩種元方法,可以實現只讀的表、具有默認值的表、繼承。
應用1:具有默認值的表
function SetDefaultValue(t, d)
local metatable = {__index = function() return d end}
setmetatable(t, metatable)
end
tab = {x=10, y=20}
print(tab.x, tab.z) -- 10 nil
SetDefaultValue(tab, 0)
print(tab.x, tab.z) -- 10 0
調用了SetDefaultValue之後,訪問tab中不存在的z字段,會先調用其__index
元方法,該方法返回0,所以有默認值。
上述寫法開銷較大,因爲這會爲每一個表都創建一個新的閉包和一個新的元表。
以下寫法能夠使得所有的表都使用一個元表:
local metatable = {__index = function (t) return t.xxx end}
function SetDefaultValue(t, d)
t.xxx = d
setmetatable(t, metatable)
end
tab = {x=10, y=20}
print(tab.x, tab.z) -- 10 nil
SetDefaultValue(tab, 0)
print(tab.x, tab.z) -- 10 0
如果擔心命名衝突,可以使用唯一的鍵:
local key = {}
local metatable = {__index = function (t) return t[key] end}
function SetDefaultValue(t, d)
t[key] = d
setmetatable(t, metatable)
end
tab = {x=10, y=20}
print(tab.x, tab.z) -- 10 nil
SetDefaultValue(tab, 0)
print(tab.x, tab.z) -- 10 0
參考資料:《Lua程序設計 第四版 Programming in Lua, Fourth Edition》