第一版是为 Lua 5.0 编写的。虽然在很大程度上仍然适用于后续版本,但有一些区别。
第四版针对 Lua 5.3,可在 Amazon 和其他书店购买。
购买本书,您还可以帮助 支持 Lua 项目


13.4.1 – __index 元方法

我之前说过,当我们访问表中不存在的字段时,结果是 nil。这是真的,但并不是全部。实际上,这种访问会触发解释器查找 __index 元方法:如果没有这种方法(通常如此),则访问结果为 nil;否则,元方法将提供结果。

这里的典型示例是继承。假设我们要创建几个描述窗口的表。每个表都必须描述几个窗口参数,例如位置、大小、配色方案等。所有这些参数都有默认值,因此我们希望仅给出非默认参数来构建窗口对象。第一个备选方案是提供一个填充不存在字段的构造函数。第二个备选方案是安排新窗口从原型窗口继承任何不存在的字段。首先,我们声明原型和构造函数,它创建共享元表的窗口

    -- create a namespace
    Window = {}
    -- create the prototype with default values
    Window.prototype = {x=0, y=0, width=100, height=100, }
    -- create a metatable
    Window.mt = {}
    -- declare the constructor function
    function Window.new (o)
      setmetatable(o, Window.mt)
      return o
    end
现在,我们定义 __index 元方法
    Window.mt.__index = function (table, key)
      return Window.prototype[key]
    end
在该代码之后,我们创建一个新窗口并查询不存在的字段
    w = Window.new{x=10, y=20}
    print(w.width)    --> 100
当 Lua 检测到 w 没有请求的字段,但有一个具有 __index 字段的元表时,Lua 会调用此 __index 元方法,参数为 w(表)和 "width"(不存在的键)。然后,元方法使用给定的键对原型进行索引并返回结果。

__index 元方法用于继承非常常见,因此 Lua 提供了一个快捷方式。尽管名称为 __index,但元方法不必是函数:它可以是表。当它是一个函数时,Lua 会使用表和不存在的键作为其参数来调用它。当它是一个表时,Lua 会在该表中重新进行访问。因此,在我们的前一个示例中,我们可以简单地将 __index 声明为

    Window.mt.__index = Window.prototype
现在,当 Lua 查找元表的 __index 字段时,它会找到 Window.prototype 的值,它是一个表。因此,Lua 会在此表中重复访问,即执行等效于
    Window.prototype["width"]
这会给出所需的结果。

将表用作 __index 元方法提供了一种实现单一继承的廉价且简单的方法。函数虽然开销更大,但提供了更大的灵活性:我们可以实现多重继承、缓存和许多其他变体。我们将在 第 16 章 中讨论这些继承形式。

当我们想要访问表而不调用其 __index 元方法时,我们使用 rawget 函数。调用 rawget(t,i) 对表 t 执行原始访问。执行原始访问不会加快代码速度(函数调用的开销会抵消任何收益),但有时您需要它,正如我们稍后将看到的。