混淆使用"。"在Lua中使用__index和命名空间表示法

时间:2014-12-17 06:52:42

标签: lua namespaces metatable meta-method

我对使用"。"

的以下两种语法感到困惑
  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
    

1 个答案:

答案 0 :(得分:5)

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

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

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

    list objects separate metatable and index table

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

    list objects with merged metatable and index table

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

    不,{}创建一个表。但是,此新表格保存在"mt"表格中的密钥Window下,可能是为了给这个Window"类"直接访问用于窗口对象的元表。只给出你展示的代码,这不是绝对必要的,你可以使用局部变量。

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

    原则上,您可以单独存储Window.mtWindow.newWindow.prototype,但如果您有多个"类,那将会很麻烦。比如Window。通过这种方式,您可以避免名称冲突,并使用Window"类"看起来更好。

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