在原型“constructor”内部或外部定义索引__index

我在阅读“Programming in Lua”上的“面向对象编程”一章时,链接为https://www.lua.org/pil/16.html

在那个例子中,他们创建了这个“构造函数”:

Account = {balance = 0}
function Account:new (o)
  o = o or {}   -- 如果用户没有提供,则创建对象
  setmetatable(o, self)
  self.__index = self
  return o
end

我在Linux上进行了一些“合成基准测试”,在包含10百万此类操作的脚本中,将 Account.__index = Account 定义在表初始化外部,比在内部快200毫秒。

我的问题是,如果我们可以在外部定义并执行一次,为什么要在每次调用此函数时在内部设置 self.__index? 继承吗?

编辑:

感谢luther给出的答案,我只是在这里为所有人创建一个示例:

local a = {}
a.__index = a
function a:foo()
    return 'foo'
end

function a:new(o)
    print(self)
    o = o or {}
    setmetatable(o, self)
    -- self.__index = self
    return o
end

local b = a:new()
-- b.__index = b
function b:bar()
    return 'bar'
end

local z = a:new()
print(z:foo()) -- 这会起作用

local y = b:new()
print(y:foo()) -- 尝试调用方法'foo'(空值)
print(y:bar()) -- 尝试调用方法'bar'(空值)

当然, y 将具有 b 作为该表的元表,但是 b 没有一个 __index 条目,该条目仅在 b 的元表中存在。如果您仍然想避免在“构造函数”内声明__index,则需要在每个派生原型或“子类”中指定。

点赞
用户3574628
用户3574628

PiL 的作者似乎尝试通过使 new 方法处理根对象与处理所有子对象相同来简化事情。这对于初学者来说可能很困惑,因为并不立即清楚 self.__index = self 经常是多余的。

此外,这种方法实际上比为每个对象添加 __index 更快。请记住,在原型系统中,每个对象都可能成为其他对象的原型。在我的机器上,进行 1e8 次试验,使用 PiL 的方法需要 14 秒,而为所有对象添加 __index 需要 23 秒。新键意味着表必须增长,因此比分配给已存在的键要慢。

令人困惑的是,这个 PiL 部分的标题是“类”,但在第一段中,他说他在模拟基于原型的语言,其中“对象没有类”。这进一步混淆了读者的期望。这个部分实现了一个自我复制的对象,而不是一个类。

这是我不太困惑,但速度较慢的实现:

Account = {balance = 0}
Account.__index = Account

function Account:new (o)
  o = o or {}   -- 如果用户未提供对象,则创建对象
  setmetatable(o, self)
  o.__index = o
  return o
end
2021-04-11 19:00:42