原文大佬链接:http://www.cnblogs.com/vanishfan/p/6909153.html
下面写一下自己能够理解的想法和思路:
1,利用元方法__newindex的特性(对字段进行赋值的时候如果表中没有则执行__newindex中的方法),新建一个空表代替原来的表,并对空表进行元方法重写实现table的相应功能。这样对任何字段的修改,在空表中肯定都找不到,只能只能统一的入口方法__newindex,我们不处理就能实现只读。
2,再利用__index元方法保存原来的表, 因为取值的时候如果找不到就会去__index中取。
利用以上两个元方法的特性实现table的只读。
自己重新写了一遍,并添加注释:
local function read_only(inputTable)
-- 避免table 互相引用,加入tempTable建立索引,存储已经处理过的table
local tempTable = {}
local function __read_only(tbl)
-- 正常如果没有互相引用,都会走处理逻辑
if not tempTable[tbl] then
-- 取到table的元表,如果没有则创建一个空table,并设置成元表,目的:下次直接可以从table的元表里取处理过的只读table
local mt = getmetatable(tbl)
if not mt then
mt = {}
setmetatable(tbl, mt)
end
-- 取到元表里的__read_only_proxy字段(处理过的只读table),没有则创建
local proxy = mt.__read_only_proxy
if not proxy then
proxy = {}
-- 再设置回去
mt.__read_only_proxy = proxy
-- 重要的处理逻辑,设置空table的元表数据
local tbl_mt = {
__index = tbl, -- 取数据
__newindex = function(t,k,v) print("can not write!") end, -- 写数据会报错
__pairs = function(t) return pairs(tbl) end, -- 重写遍历
__len = function(t) return #tbl end, -- 重写获取长度
__read_only_proxy = proxy, -- 这里也设置一下,方便获取
}
setmetatable(proxy, tbl_mt)
end
-- 保存
tempTable[tbl] = proxy
-- 递归处理table里面的table
for k, v in pairs(tbl) do
if type(v) == "table" then
tempTable[k] = __read_only(v)
end
end
end
-- 返回保存的数据
return tempTable[tbl]
end
return __read_only(inputTable)
end
测试:
local t0 = {key1 = 1, key2 = 2}
local t1 = read_only(t0)
for i,v in pairs(t1) do
print("key: " .. i .. "\t v: " .. v)
end
--[[
key: key2 v: 2
key: key1 v: 1
]]
print(#t1) -- 0
t1.key1 = 3 -- can not write!