2

我对使用“。”的以下两种语法感到困惑。

  1. 据我了解,__index当键不存在于表中但存在于其元表中时调用。那么为什么列表表调用__index然后将自身分配给list.__index

    list = {}
    list.__index = list
    
    setmetatable(list, { __call = function(_, ...)
    local t = setmetatable({length = 0}, list)
      for _, v in ipairs{...} do t:push(v) end
      return t
    end })
    
    function list:push(t)
      if self.last then
        self.last._next = t
        t._prev = self.last
        self.last = t
      else
       self.first = t
       self.last = t
      end
      self.length = self.length + 1
    end 
      .
      .
      .
    local l = list({ 2 }, {3}, {4}, { 5 })
    
  2. 是否Window.mt只是创建一个表?为什么我们需要Window = {}在这里作为命名空间?

    Window = {}  -- create a namespace    
    Window.mt = {}  -- create a metatable
    Window.prototype = {x=0, y=0, width=100, height=100, } 
    
    function Window.new (o)  
        setmetatable(o, Window.mt)
        return o
    end
    
    Window.mt.__index = function (table, key)
        return Window.prototype[key]
    end
    
    w = Window.new{x=10, y=20}
    print(w.width)    --> 100
    
4

1 回答 1

5

那么为什么列表表调用__index,然后将自己分配给list.__index?

您的代码中没有任何地方调用 __index列表表。然而,分配部分是一个常见的 Lua 习惯用法(又名 hack),以节省一些内存。从概念上讲,涉及 4 种不同类型的表:

  1. 列出对象(通过{length=0}在您的代码中创建的表)
  2. __index当您尝试访问对象中不存在的字段时修改列表对象的行为的元表(包含字段)
  3. list类,它包含列表对象的所有方法(如方法push),并且还用作列表对象的构造函数
  4. 类的元表(包含__call字段)list,以便您可以list像调用函数一样调用表

    列出对象单独的元表和索引表

由于元表字段总是以两个下划线 ( __) 开头,而普通方法通常不会,因此您可以将元表字段和普通方法并排放在一个表中而不会发生冲突。这就是这里发生的事情。list类表还用作列表对象的元表。因此,使用这个技巧,您可以节省通常需要用于单独元表的内存(x86-64 Linux 上 Lua 5.2 的字节大小显示在表格标题栏中的方括号中,顺便说一句。):

列出具有合并元表和索引表的对象

Window.mt 是否只是创建一个表?

不,{}创建一个表。但是,这个新表保存在表中的键下"mt"Window可能是为了让这个Window“类”的用户直接访问用于窗口对象的元表。仅给定您显示的代码,这并不是绝对必要的,您可以使用局部变量。

为什么我们在这里需要 Window = {} 作为命名空间?

原则上,您可以分别存储Window.mtWindow.newWindow.prototype,但是如果您有多个“类”,例如Window. 这样您可以避免名称冲突,并且使用Window“类”看起来更好。

另一个原因可能是require只能从模块定义中返回单个值,并且如果要从模块中导出多个值(如newmtprototype),则需要一个表将它们包装在一起(或使用全局变量,但是被认为是不好的风格)。

于 2014-12-17T11:58:56.507 回答