lua table继承和克隆的骚操作(二)

背景

为了实现上篇最后所讲的创建新table的方法,需要解决如下几个问题:

  1. 如何支持设置原table中的值为nil
  2. 如何支持使用pairs遍历新table

代理表

支持设置原table中的值为nil的方案,首先得引入一个另外的方案 table代理的支持这个table代理能够让每次访问代理的调用都通过代理上的元表,实现如下:

function createNewTable(parent)
    local values = {} -- 用来存放新的值

    -- 代理表,每次通过这个表访问和赋值都会经过下面元表的__index和__newindex方法
    -- 新的值会存储到values表中,访问的时候先从values表中查找,如果没有找到,在去parent中查找
    local proxy =
        setmetatable(
        {},
        {
            __index = function(__, k)
                local retValue = values[k]
                if retValue ~= nil then
                    return retValue
                end
                return parent[k]
            end,
            __newindex = function(__, k, v)
                values[k] = v
            end
        }
    )
    return proxy
end

local tbA = {a = 1}
local tbB = createNewTable(tbA)
tbB.b = 2

print(tbB.a) -- 1
print(tbB.b) -- 2

tbB.a = 3
tbB.b = 4
print(tbB.a) -- 3
print(tbB.b) -- 4

tbB.a = nil
tbB.b = nil
print(tbB.a) -- 1
print(tbB.b) -- nil

支持赋值为nil

从代理表的实现以及测试结果来看,代理表本身也是无法实现赋值原table中存在的数值为nil的。所以在此基础上还需要引入专门的存储哪些字段为nil的table,实现如下:

function createNewTable(parent)
    local values = {} -- 用来存放新的值
    local nils = {} -- 存放空值

    -- 代理表,每次通过这个表访问和赋值都会经过下面元表的__index和__newindex方法
    -- 新的值会存储到values表中,访问的时候先从values表中查找,如果没有找到,在去parent中查找
    local proxy =
        setmetatable(
        {},
        {
            __index = function(__, k)
                if nils[k] then -- 只要存在于nils中,直接返回nil
                    return nil
                end

                local retValue = values[k]
                if retValue ~= nil then
                    return retValue
                end
                return parent[k]
            end,
            __newindex = function(__, k, v)
                if v == nil then -- 如果v为nil,设置nils表中的k为true, 并且设置 values表中的k为nil
                    nils[k] = true
                    values[k] = nil
                else
                    values[k] = v
                end
            end
        }
    )
    return proxy
end

local tbA = {a = 1}
local tbB = createNewTable(tbA)
tbB.b = 2

print(tbB.a) -- 1
print(tbB.b) -- 2

tbB.a = 3
tbB.b = 4
print(tbB.a) -- 3
print(tbB.b) -- 4

tbB.a = nil
tbB.b = nil
print(tbB.a) -- nil
print(tbB.b) -- nil

如此,便实现了设置原table中存在的值为nil的需求,但是依然不支持遍历

预告

所以在下一篇中会实现,遍历这样的一张表

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。