1. 元表 (Metatable)
元表是普通的 Lua 表,定义了原始值在某些特定操作下的行为。 我们称元表中的键为事件,称值为元方法。
设置 (SetMetatable)/获取 (GetMetatable) 元表:
设置元表的两种写法:
local mt = {}
setmetatable(t, mt)
setmetatable(t, {__add = function() end})
local mt = {}
local t = {}
print(getmetatable(t)) -- nil
setmetatable(t, mt) -- 设置表 t 的元表为 mt
print(mt)
print(getmetatable(t)) -- 获取元表地址,和上面打印的 mt 地址相同
输出结果:
nil table: 000001ECD17E3940 table: 000001ECD17E3940
2. 元方法
2.1 __add(对表进行'+'操作时触发)
local mt = {}
mt.__add = function(tab1, tab2)
local resTab = {}
local len = math.max(#tab1, #tab2)
if len == 0 then
return resTab
end
for i = 1, len, 1 do
if type(tab1[i]) == "nil" then tab1[i] = 0 end
if type(tab2[i]) == "nil" then tab2[i] = 0 end
resTab[i] = tab1[i] + tab2[i]
end
return resTab
end
local tab1 = {1, 2, 3}
local tab2 = {4, 5, 6}
setmetatable(tab1, mt)
setmetatable(tab2, mt)
local resTab = tab1 + tab2
for index, value in ipairs(resTab) do
print(value)
end
输出结果:
5
7
9
| 元方法 | 对应运算符 |
|---|---|
| __add | + |
| __sub | - |
| __mul | * |
| __div | / |
| __mod | % |
| __concat | …(连接符) |
| __eq | == |
| __lt | < |
| __le | <= |
注意事项 当涉及二元操作符时:两表元表有以下情况
- 相同元表:执行元表中的元方法
- 不同元表:第一个值有元表(操作符前面),就以这个元表为准看是否有元方法,如果没有就看第二个元表是否有元方法。都没有元表:报错
local mt1 = {}
local mt2 = {}
mt1.__sub = function(t1, t2) print("mt1->sub") end
mt2.__sub = function(t1, t2) print("mt2->sub") end
local t1 = {}
local t2 = {}
local t3 = {}
setmetatable(t1, mt1) -- 设置 mt1 为 t1(普通表)的元表
setmetatable(t2, mt2) -- 设置 mt2 为 t2(普遍表)的元表
local res1 = t1 - t2 -- 计算过程中调用元表的 mt1.__sub 元方法
print("mt1->sub") -- 模拟输出
local res2 = t2 - t1 -- 计算过程中调用元表的 mt2.__sub 元方法
print("mt2->sub") -- 模拟输出
local res3 = t3 - t1 -- 由于 t3 没有元表 __sub 元方法,所以调用 t1 的元表中的元方法
print("mt1->sub") -- 模拟输出
2.2 __tostring(print() 时触发)
local mt = {}
mt.__tostring = function(tab)
local str = ""
for index, value in pairs(tab) do
str = str .. index .. "--" .. value .. "\n"
end
return str
end
local tab = {1, 2, 3}
setmetatable(tab, mt)
print(tab)
输出结果:
1--1
2--2
3--3
2.3 __index(访问表中不存在属性时触发)
- __index 是一个表
local mt = {}
mt.__index = {sex = "boy"}
local tab = {name = "ming", age = 18}
print(tab.name, tab.age) -- ming 18
print(tab.sex) -- nil
setmetatable(tab, mt)
print(tab.sex) -- boy
print(tab.enjoy) -- nil
- __index 是一个函数
local mt = {}
mt.__index = function(table, key)
print(table) -- 打印 t 表
print(key) -- 打印 name
end
local t = {}
setmetatable(t, mt)
print(t.name) -- t 表不可以找到,调用元表的 __index 元方法
输出结果:
table: 0000026EFEE9F4C0 name nil
2.4 __newindex(对 table 中不存在的字段赋值时调用)
- 普通表修改键值 存在则修改,不存在就会增加
local t = {name = "Y"}
t.name = "YY" -- 修改
t.age = 23 -- 添加
for key, value in pairs(t) do -- 无序遍历字典类型表
print(key .. " : " .. value)
end
输出结果:
age : 23
name : YY
- __newindex 是一个函数 调用 __newindex 函数,但是不会给元表赋值
local mt = {}
mt.__newindex = function(table, key, value)
print(table)
print("key:" .. key)
print("value:" .. value)
end
local t = {}
setmetatable(t, mt)
print(t.name) -- nil
t.name = "YY"
print(t.name) -- 赋值后先调用__newindex 函数然后打印 nil
输出结果:
nil
table: 0000012C2529F5F0
key:name
value:YY
nil
- __newindex 是一个表 如果普通表没有该变量,那么到元表查找,并在元表新增或修改变量值
local k = {name = "ming"}
local mt = {}
mt.__newindex = k
local t = {}
setmetatable(t, mt)
print(t.name) -- nil 查询
print(k.name) -- Y 查询
t.name = "YY" -- 到对应的 k 表修改
print(k.name) -- YY 查询
print(t.name) -- nil 查询
输出结果:
nil
ming
YY
nil

