目录
[(5)__index的介绍 这个很重要](#(5)__index的介绍 这个很重要)
[(7)针对上面两种设计 还有反他们的设计 主要都是为了不时之需 那就是 rawget 和 rawset](#(7)针对上面两种设计 还有反他们的设计 主要都是为了不时之需 那就是 rawget 和 rawset)
一、Lua中的元表的作用以及设计理念
元表(metatable)是 Lua 里赋予表"扩展行为"的机制。因为 Lua 只有 table 这一种复合数据结构,官方希望在不引入复杂类型系统的前提下,也能做到运算符重载、自定义索引、对象模型等能力,所以设计了"把额外规则存进元表"的办法。任何 table 都可以附着一张元表;Lua 遇到无法直接处理的操作(例如对不存在的键取值、两个表相加、把 table 当函数调用)时,就去检查元表里对应的"元方法"(如 __index、__add、__call,这里具体的我们后面讲),如果存在就按你定义的逻辑来执行。
概念上,元表就是"描述某个 table 特殊行为的表"。它本身也是普通 table,只是被 Lua 引擎当作当前表的"说明书"。
其实元表也是一张表,当你对当前表的一些操作无法得到满足的时候 会去对你的元表有一定操作。
元表的初衷是:让简单的 table 也能拥有"像别的语言里类/对象那样的可定制行为",为 Lua 的灵活性和简洁语法提供底层支撑。
二、关于元表的操作
(1)设置一个表为另一个表的元表
很简单 申明两个表 然后用一个setmetatable(被设置表,元表)就完了
Lua
-- 设置元表
meta = {}
myTable = {}
-- 设置元表函数
-- 第一个参数 子表 第二个参数 子表的元表
setmetatable(myTable, meta)
(2)__tostring方法的介绍
这个是什么呢?这个是 如果你有一个子表 你想对他进行打印操作 比如print(mytable) 如果有元表 那么就会执行 元表中的这个函数 不过这个函数默认是空的 需要我们自己写内容 比如:
不设置元表情况 设置元表情况


无参数情况 有参数情况


有一点需要注意的是 你可以将子表作为参数传进去
(3)__call方法的介绍
这个很简单 如果把子表当成函数调用时候
会调用执行这个call方法

这里解释一下第一个 true哪里来的 刚刚我们说了 如果设置了元表的子表有打印行为就会执行元表的__tostring()函数 这里就是这么来的
这个也是一样 默认传入的第一个参数就是子表
(4)一堆运算符的重载
不难 原理同上 下面是栗子🌰!值得关注的是 必须是同一元表 相加才有意义 不然没用还可能报错
Lua
meta4 = {
-- 相当于运算符重载 当子表使用+运算符时 会调用该方法
-- 运算符+
__add = function (t1,t2)
return t1.age + t2.age
end,
-- 运算符-
__sub = function (t1,t2)
return t1.age - t2.age
end,
-- 运算符*
__mul = function (t1,t2)
return t1.age * t2.age
end,
-- 运算符/
__div = function (t1,t2)
return t1.age / t2.age
end,
-- 运算符%
__mod = function (t1,t2)
return t1.age % t2.age
end,
-- 运算符^
__pow = function (t1,t2)
return t1.age ^ t2.age
end,
-- 运算符==
__eq = function (t1,t2)
return t1.age == t2.age
end,
-- 运算符<
__lt = function (t1,t2)
return t1.age < t2.age
end,
-- 运算符<=
__le = function (t1,t2)
return t1.age <= t2.age
end,
-- 运算符..
__concat = function (t1,t2)
return tostring(t1.age)..tostring(t2.age)
end
}
myTable4 = {
age = 10
}
setmetatable(myTable4,meta4)
myTable5 = {
age = 2
}
setmetatable(myTable5,meta4)
print(myTable4 + myTable5) -- 12
print(myTable4 - myTable5) -- 8
print(myTable4 * myTable5) -- 20
print(myTable4 / myTable5) -- 5
print(myTable4 % myTable5) -- 0
print(myTable4 ^ myTable5) -- 100
print(myTable4 == myTable5) -- false
print(myTable4 < myTable5) -- false
print(myTable4 <= myTable5) -- false
print(myTable4 .. myTable5) -- 102
-- -- 注意 ! 如果要用条件运算符 来比较两个对象
-- -- 那么这两个对象的原表一定要一致 才能准确调用重载的运算符 不然没用

(5)__index的介绍 这个很重要
这个的作用针对的是表中字段的查找操作
这个的作用就是 如果你在你当前表查找某个元素 而你这个表没有这个元素的话 那么就会到你的元表指定的__index的表进行元素的查找 及比如 子表A 的元表metaA的__index指向的表为 F。 F中有一个字段为 M。A表没有,如果我直接访问A.M那么这个肯定是nil的 ,如果我设置了A 的元表metaA的__index为F的话 那么就可以获取到这个字段了
记住啊 是到元表指定的表 而不是找不到元素来元表找
说白了 就是找不到东西会到原表指定表中继续查找 对于后面的面向对象中很有用
Lua
-- __index的作用就是 当子表中找不到某个属性时候
-- 会到原表中__index指定的表中去寻找对应的属性
-- 如果找不到的话 就为空了
meta6 = {
name = "元表的名字"
}
myTable6 = {
-- name = "子表的名字"
}
-- 这个index一般写在表外面 内部可能会出现奇怪的问题 不知道有什么问题 我也看到过写里面的
-- 这个index的查找可以一层层的往上找 一定是找__index指向的表
meta6.__index = {name = "12345"}
setmetatable(myTable6,meta6)
print(myTable6.name)

(6)__newindex的介绍
这个的作用针对的是给表中字段赋值操作 如果给表中不存在字段赋值的话 会将这个字段转移到 你的元表的 __newindex指向的表中 创建这样一个字段然后赋值
触发时机:对表 t 写入一个目前不存在的 key 时(例如 t.x = 10 且 t.x == nil),Lua 会检查 t 的元表,如果定义了 __newindex 则改由它处理赋值
Lua
-- newindex 当一个表中的属性赋值的时候 这个属性不存在的话
-- 那么会把这个值赋值到newindex所指的表中 不会修改自己
meta7 = {
}
meta7.__newindex = meta7
myTable7 = {
}
setmetatable(myTable7,meta7)
myTable7.age = 1000
print(myTable7.age)
print(meta7.age)

(7)针对上面两种设计 还有反他们的设计 主要都是为了不时之需 那就是 rawget 和 rawset
rawget 当我们使用这个函数时 会去找自己身上有没有这个变量 忽略index的设计
rawset 该方法会忽略newindex的设置 只会改自己的表中的变量
还有个小方法 获取自己的元表是谁 getmetatable(表)
写完收工 明日再见