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

LED*_*tom 2 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 })
    
    Run Code Online (Sandbox Code Playgroud)
  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
    
    Run Code Online (Sandbox Code Playgroud)

sif*_*joe 5

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

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

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

    list对象分开metatable和index表

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

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

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

不,{}创建一个表.然而,这个新表下键保存"mt"Window表中,可能给这个用户的Window"类"直接访问的元表所使用的窗口对象.只给出您显示的代码,这不是绝对必要的,您可以使用局部变量.

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

原则上,你可以存储Window.mt,Window.newWindow.prototype单独的,但会很麻烦,如果你有多个"类"之类Window.这样你可以避免名字冲突,并使用Window"类"看起来更好.

另一个原因可能是,require只能从模块定义返回一个值,如果你要导出多个值(例如new,mtprototype)由一个模块,你需要一个表来包装在一起(或使用全局变量,但被认为是坏风格).