元表的概念
- 任何表变量都可以作为另一个表变量的元表
- 任何表变量都可以有自己的元表
- 当我们在子表中进行一些特定的操作时,会执行元表中的内容
设置和获取元表
--设置元表
--setmetatable(子表,元表)
meta = {}
t1 = {}
setmetatable(t1, meta)
--获取元表
print(getmetatable(t1)) -- table: 00E09968
元表中的元方法
__tostring
当子表要被当作字符串使用时,会调用它的元表的__tostring方法
--__tostring 当子表要被当作字符串使用时,会调用它的元表的__tostring方法
meta2 = {
__tostring = function()
return "小明"
end
}
t2 = {}
setmetatable(t2, meta2)
print(t2) -- 小明
meta3 = {
__tostring = function(t)
return t.name
end
}
t3 ={
name = "小芳"
}
setmetatable(t3, meta3)
print(t3) -- 小芳
__call
当子表被当作一个函数来使用时,会默认调用元表的__call方法
meta4 = {
__tostring = function(t)
return t.name
end,
-- __call被调用时,第一个参数默认是子表,从第二个参数开始才是调用时传入的参数
__call = function(t, a)
print(t) -- 之类输出小红的原因是因为将字表作为字符串输出了,且上面定义了tostring的元方法
print(a)
end
}
t4 ={
name = "小红"
}
setmetatable(t4, meta4)
t4(666) -- 小红 666
运算符重载
- __add 加法的运算符重载,当对两个表使用+连接时,会调用__add元方法
- __sub 减法
- __mul 乘法
- __div 除法
- __mod 取余
- ___pow 幂运算
- __eq 等于等于
- __lt 小于
- __le 小于等于
- __concat 相连
注意:在两个表的元表不同时,调用==、<=这些比较运算符时会出问题。需要让两个表的元表设置一致才能正确调用。
--__add 加法的运算符重载。当对两个表使用+连接时,会调用__add元方法
--__sub 减法-
--__mul 乘法*
--__div 除法/
--__mod 取余%
--__pow 幂运算^
--__eq 等于等于==
--__lt 小于<
--__le 小于等于<=
--__concat 相连..
meta5 = {
__add = function(t1, t2)
return t1.index + t2.index
end,
__sub = function(t1, t2)
return t1.index - t2.index
end,
__mul = function(t1, t2)
return t1.index * t2.index
end,
__div = function(t1, t2)
return t1.index / t2.index
end,
__mod = function(t1, t2)
return t1.index % t2.index
end,
__pow = function(t1, t2)
return t1.index ^ t2.index
end,
__eq = function(t1, t2)
return t1.index == t2.index
end,
__lt = function(t1, t2)
return t1.index < t2.index
end,
__le = function(t1, t2)
return t1.index <= t2.index
end,
__concat = function(t1, t2)
return t1.index .. " " .. t2.index
end
}
t5 = {index = 5}
t6 = {index = 6}
setmetatable(t5, meta5) -- 注意,大部分运算符只需要其中一个表设置了元表即可
print(t5 + t6) -- 11
print(t5 - t6) -- -1
print(t5 * t6) -- 30
print(t5 / t6) -- 0.8333333
print(t5 % t6) -- 5
print(t5 ^ t6) -- 15625
print(t5 .. t6) -- 5 6
--在两个表的元表不同时,调用==、<=这些比较运算符时会出问题
--print(t5 < t6) -- error
--想要正确使用运算符重载的元方法,需要让两个表的元表设置一致
setmetatable(t6, meta5)
print(t5 == t6) -- false
print(t5 < t6) -- true
print(t5 <= t6) -- true
--元方法中并没有>和>=,但是Lua会自动调用<和<=取非的结果返回
print(t5 > t6) -- false
print(t5 >= t6) -- false
__index和__newindex
__index
- 当子表中找不到某一个属性时,会到元表中__index指向的表中去找索引
- __index可以设置为表自身,这样就可以直接访问元表中的元素而不用再定义一个__index了
- __index还可以层层嵌套。当在元表中的__index找不到目标元素时,会继续向上访问元表的元表的__index
--__index 当子表中找不到某一个属性时,会到元表中__index指向的表中去找索引
meta7 = {}
meta7.__index = {age = 2} -- __index尽量在表外进行赋值。在表内赋值有时候会有问题
t7 = {}
setmetatable(t7, meta7)
print(t7.age) -- 2
--__index可以设置为表自身,这样就可以直接访问元表中的元素而不用再定义一个__index了
meta8 = {age = 3}
meta8.__index = meta8
t8 = {}
setmetatable(t8, meta8)
print(t8.age) -- 3
--__index还可以层层嵌套。当在元表中的__index找不到目标元素时,会继续向上访问元表的元表的__index
meta9Father = {age = 4}
meta9Father.__index = meta9Father
meta9 = {}
meta9.__index = meta9
t9 = {}
setmetatable(t9, meta9)
setmetatable(meta9, meta9Father)
print(t9.age) -- 4
__newindex
--__newindex 当赋值时,如果赋值一个不存在的索引,那么会把这个值赋值到newindex所指的表中,不会修改自己的表
meta10 = {}
meta10.__newindex = {}
t10 = {}
setmetatable(t10, meta10)
t10.age = 5
print(t10.age) -- nil
print(meta10.__newindex.age) -- 5
rawget和rawset
rawget
避过__index,只在自己本表中查找目标元素
--rawget 避过__index,只在自己本表中查找目标元素
meta11 = {}
meta11.__index = {age = 6}
t11 = {}
setmetatable(t11, meta11)
print(t11.age) -- 6
print(rawget(t11, age)) -- nil
rawset
避过__newindex,只在自己本表中进行赋值
--rawset 避过__newindex,只在自己本表中进行赋值
meta12 = {}
meta12.__newindex = {}
t12 = {}
setmetatable(t12, meta12)
t12.age = 7
rawset(t12, "height", 180)
print(t12.age) -- nil
print(t12.height) -- 180