改变对象的 metamethods

local Class = {}
Class.__meta = {__index=Class}
function Class.new() return setmetatable({}, Class.__meta)

假设我们想要使用 metatable 改变单个实例 object = Class.new() 的行为,

有一些错误需要避免:

setmetatable(object, {__call = table.concat}) -- WRONG

这将旧的 metatable 与新的 metatable 交换,从而打破了类的继承

getmetatable(object).__call = table.concat -- WRONG AGAIN

请记住,表仅供参考; 实际上,对象的所有实例只有一个实际表,除非构造函数定义为 1 ,因此通过这样做我们修改了类的所有实例的行为。

这样做的一种正确方法:

不改变类:

setmetatable(
    object,
    setmetatable(
        {__call=table.concat},
        {__index=getmetatable(object)}
    )
)

这是如何运作的? - 我们在错误#1 中创建了一个新的 metatable,但是我们不是将它留空,而是为原始 metatable 创建一个软拷贝。可以说新的 metatable 从原始的继承,好像它本身就是一个类实例。我们现在可以覆盖原始元表的值而无需修改它们。

改变类:

1 号(推荐):

local __instance_meta = {__index = Class.__meta}
-- metatable for the metatable
-- As you can see, lua can get very meta very fast
function Class.new()
    return setmetatable({}, setmetatable({}, __instance_meta))
end

2(少推荐):见 1

1 function Class.new() return setmetatable({}, {__index=Class}) end