一个普通的表中所有字段的默认值都是 nil
。通过元表,可以很容易地修改这个默认值:
function setDefault (t, d) local mt = {__index = function() return d end} setmetatable(t, mt) end tab = {x = 10, y = 20} print(tab.x, tab.z) --> 10 nil setDefault(tab, 0) print(table.x, tab.z) --> 10 0
在调用 setDefault
后,任何对表 tab
中不存在字段的访问都将调用它的 __index
方法,而这个元方法会返回 0
(这个元方法中的值是 d
)。
函数 setDefault
为所有需要默认值的表创建了一个新的闭包和一个新的元表。如果我们有很多需要默认值的表,那么开销会比较大。然而,由于具有默认值 d
的元表是与元方法关联在一起的,所以我们不能把同一个元表用于具有不同默认值的表。为了能够使所有的表都使用同一个元表,可以使用一个额外的字段将每个表的默认值存放到表自身中。如果不担心命名冲突的话,我们可以使用形如 "___"
这样的键作为额外的字段:
local mt = {__index = function (t) return t.___ end} function setDefault (t, d) t.___ = d setmetatable(t, mt) end
注意
这里我们只在 setDefault
外创建了一次元表 mt
及对应的元方法
如果担心命名冲突,要确保这个特殊键的唯一性也很容易,只需要创建一个新的排除表,然后将它作为键即可:
local key = {} local mt = {__index = function (t) return t[key] end} function setDefault (t, d) t[key] = d setmetatable(t, mt) end
还有一种方法可以将每个表与其默认值关联起来,称为对偶表示( dual representation
),即使用一个独立的表,该表的键为各种表,值为这些表的默认值。不过,为了正确地实现这种做法,我们还需要一种特殊的表,称为弱引用表( weak table
)。
另一种为具有相同默认值的表复用同一个元表的方式是记忆元表。不过,这也需要用到弱引用表。
关于弱引用表,请转至笔记弱引用表