本文大量参考OpenResty最佳实践
-->>>>>>>>>>>>>>>> 元表
-- 元表是用来定义table或userdata操作方式的表,可以用来实现面向对象
-- 元表的设置
-- 定义元表
local t1 = {1,2,3}
local t2 = {2}
local mt = {}
-- 定义元表mt.__add元方法
mt.__add = function(t1,t2)
local temp = {}
for _,v in pairs(t1) do
table.insert(temp,v)
end
for _,v in pairs(t2) do
table.insert(temp,v)
end
return temp
end
-- 设置t1的元表为mt
setmetatable(t1,mt)
local t3 = t1+t2 --> 实际上t3 = t2+t1也是可以的。编译器会查看t1是否有元表,并查看t1的元表是否有__add元方法,若有则调用;查看t2是否有元表,并查看t2的元表是否有__add元方法,若有则调用
-- 输出t3
local st = "{"
for _,v in pairs(t3) do
st = st..v..", "
end
st = st.."}"
print(st)
--结果
--{1, 2, 3, 2, }
-- 实际上还可以对__add元方法进行重载,重载的第一个参数时self,但是这里的重载导致之后的__call方法如果不重新使用setmetatable(t1,mt)会报错,如果不重载就不会,
local union = function(self,another)
local temp = {}
for _,v in pairs(another) do
table.insert(temp,v)
end
for _,v in pairs(self) do
table.insert(temp,v)
end
return temp
end
setmetatable(t1, {__add = union})
local t4 = t1+t2
local st = "{"
for _,v in pairs(t4) do
st = st..v..", "
end
st = st.."}"
print(st)
--结果:
--{2, 1, 2, 3, }
-- 定义__call可以让table当做一个函数来使用
mt.__call = function(mytable,...)
-- 输出所有的参数
for _,v in ipairs{...} do
print(v)
end
end
setmetatable(t1,mt)
t1(1,2,3)
--结果
--1
--2
--3
-- 修改__tostring可以修改table转化为字符串的行为
print(t1)
mt.__tostring = function(t)
local s = "{{"
for i,v in ipairs(t) do
if i > 1 then
s = s..","
end
s = s..v
end
s = s.."}}"
return s
end
print(t1)
--结果:
--table: 0x000cbc80
--{{1,2,3}}
-- 当调用到table一个不存在的索引是,会使用到元表的__index元方法,和之前方法不一样的是,__index既可以是函数也可以是table
print(t1.key)
mt.__index = function(t,key)
return "it is "..key
end
print(t1.key)
mt.__index = {key = "it is a beautiful key"}
print(t1.key)
print(t1.key2)
--结果:
--nil
--it is key
--it is a beautiful key
--nil
--当为table中一个不存在的索引赋值时,会调用元表中的__newindex元方法
mt.__newindex = function(t,index,value)
print("index is "..index)
print("value is "..value)
end
t1 = {key = "it is a hello key"}
setmetatable(t1,mt) --> 此时的t1已经属于新创建的t1,因此需要重新设置元表
print(t1.key)
t1.newKey = 10 --> 实际中表的newKey索引值还是为空,上面只是调用了元表的__newIndex方法,输出了参数信息
print(t1.newKey)
--结果:
--it is a hello key
--index is newKey
--value is 10
--nil
--在__neweindex作为一个table时,为不存在的索引赋值会导致该索引和赋值被赋到__newindex所指向的表中,不会对原来的表进行修改
local newTable = {}
mt.__newindex = newTable
t1 = {}
setmetatable(t1,mt)
print(t1.newKey,newTable.newKey)
t1.newKey = "it is a hello kitty"
print(t1.newKey,newTable.newKey)
--结果:
--nil nil
--nil it is a hello kitty
-- rawget以及rawset可以直接获取表中索引的实际值,并进行赋值,而不受元方法影响
mt.__index = {key = "it is hhkey"}
t1 = {}
setmetatable(t1,mt)
print(t1.key)
--通过rawget可以直接获取t中的key索引,不受__index元方法的影响
print(rawget(t1,"key"))
--结果:
--it is hhkey
--nil
--rawset可以直接为表中索引赋值,而不通过元表的__newindex元方法
local newTable = {}
mt.__newindex = newTable
t1 = {}
setmetatable(t1,mt)
print(t1.newKey,newTable.newKey)
rawset(t1,"newKey","it is 233key")
print(t1.newKey,newTable.newKey)
--结果:
--nil nil
--it is 233key nil
-- 如果想保护对象对其使用者既看不到也不能修改metatables,
-- 我们可以对metatable设置__metatable的值,getmetatable将会返回这个值,而调用setmetatable将会出错
local t1 = setmetatable({},{__metatable="You cannnot access here"})
print(getmetatable(t1))
-- setmetatable(t1,{}) -- 此时该语句会引发编译器报错