第一版是针对 Lua 5.0 编写的。虽然对于后续版本来说仍然非常相关,但还是有一些区别。
第四版针对 Lua 5.3,可在 Amazon 和其他书店购买。
购买本书,您还可以帮助支持 Lua 项目


13.4.4 – 跟踪表访问

__index__newindex 仅在表中不存在索引时才相关。捕获对表的所有访问的唯一方法是保持表为空。因此,如果我们想要监视对表的全部访问,我们应该为真实表创建一个代理。此代理是一个空表,具有适当的 __index__newindex 元方法,它们会跟踪所有访问并将它们重定向到原始表。假设 t 是我们要跟踪的原始表。我们可以编写类似这样的代码

    t = {}   -- original table (created somewhere)
    
    -- keep a private access to original table
    local _t = t
    
    -- create proxy
    t = {}
    
    -- create metatable
    local mt = {
      __index = function (t,k)
        print("*access to element " .. tostring(k))
        return _t[k]   -- access the original table
      end,
    
      __newindex = function (t,k,v)
        print("*update of element " .. tostring(k) ..
                             " to " .. tostring(v))
        _t[k] = v   -- update original table
      end
    }
    setmetatable(t, mt)
此代码会跟踪对 t 的每次访问
    > t[2] = 'hello'
    *update of element 2 to hello
    > print(t[2])
    *access to element 2
    hello
(请注意,遗憾的是,此方案不允许我们遍历表。pairs 函数将在代理上运行,而不是在原始表上运行。)

如果我们想要监视多个表,我们不需要为每个表使用不同的元表。相反,我们可以将每个代理与它的原始表相关联,并为所有代理共享一个公共元表。将代理与表相关联的一种简单方法是将原始表保存在代理的字段中,只要我们能确保该字段不会用于其他目的即可。确保这一点的一种简单方法是创建一个私钥,其他人无法访问。将这些想法放在一起,得到以下代码

    -- create private index
    local index = {}
    
    -- create metatable
    local mt = {
      __index = function (t,k)
        print("*access to element " .. tostring(k))
        return t[index][k]   -- access the original table
      end,
    
      __newindex = function (t,k,v)
        print("*update of element " .. tostring(k) ..
                             " to " .. tostring(v))
        t[index][k] = v   -- update original table
      end
    }
    
    function track (t)
      local proxy = {}
      proxy[index] = t
      setmetatable(proxy, mt)
      return proxy
    end
现在,每当我们想要监控一个表 t 时,我们所需要做的就是 t = track(t)