一 Lua元表的理解
prototype 属性使您有能力向'对象' '添加' 属性和方法 -->"扩展功能的作用"
(2)理解1
说明:元表只针对'table'和'userdata'
(3)理解2
1) '甲[弹弓]' --> '乙[剑]' --> '丙[盾牌]'
理解:我没有,就找'爸爸[proptopyte]',爸爸没有就找'爷爷[proptopyte]',再没有,就'结束'
(4)理解3
(5) 个人理解
1) lua的'元表'是附加在table上的一个'额外'的属性-->这个属性也是'一张表'
重点:元表'也是'table
2) 作用:给tale提供一些'额外'的属性或功能
3) 场景1:'模拟'动态语言中的'OOP(面向对象)'的特性,理解上类似JS的'prototype'机制
二 元表
引出: 在 Lua table 中我们可以'访问'对应的key来得到value值,但是却'无法'对两个 'table' 进行操作
metamethod是'metatable'的方法
(1)元表的操作
1) 为一个table'设置'metatable
setmetatable(table,matatable)
2) '取出'一个table的metatable
getmetatable(table)
备注: 单纯的表'作为元表'并没有'太大的意义',我们需要的是一个'实现了元方法的表'作为'元表'
getmetatable(table): '返回'对象的元表(metatable)
1) 语法: setmetatable('old_table','metatable') -->"原始表和元表关联"
2) 功能: 对指定table'设置元表(metatable)',如果元表(metatable)中'存在__metatable'键值,setmetatable会'失败'
1) 无"__metatable"字段的元表
2)有"__metatable"字段的元表
+++++++++++++"应用场景"+++++++++++++
保护元表:使用户既'不能看到'、也'不能修改'集合的元表
具体表现:如果在元表中设置'__metatable'字段,那么getmetatable会'返回'这个字段的值,而setmetatable则会'引发一个错误[p219,暂时未发现]'
(2)其他类型的元表
1) 一个表可以是'任意类型值'的元表;但是在Lua语言中,只能为'表'设置'元表' -->"重点"
2) 为'其他类型[非table]'的值设置元表,必须通过'C代码'或'调试库'完成
目的:防止'过渡使用'某种类型的'所有值生效'的元表
一个表可以成为它'自身'的表
三 元方法
元方法实际上是table的一些'保留键',一般'格式'为:__xx -->"两个下划线开头"
我们可以为这些键'赋值'为'函数[重点]';部分元方法可以'设为table'
重点: 每种'运算符'都有一个对应的'元方法'
(1) Lua内建约定的元方法的全部清单
__add(a, b) 对应表达式 a + b 调用 --> "重点1"
eg:__add这是'MetaMethod(元方法)'
__sub(a, b) 对应表达式 a - b
__mul(a, b) 对应表达式 a * b
__div(a, b) 对应表达式 a / b
__mod(a, b) 对应表达式 a % b --> "取模"
__pow(a, b) 对应表达式 a ^ b --> "幂运算"
__unm(a) 对应表达式 -a
__concat(a, b) 对应表达式 a .. b --> "重点2"
__len(a) 对应表达式 #a --> "重点3"
+++++++++++++++"关系运算符"+++++++++++++++
__eq(a, b) 对应表达式 a == b
__lt(a, b) 对应表达式 a < b
__le(a, b) 对应表达式 a <= b
__index(a, b) 对应表达式 a.b --> "重点4"
解读:"先在当前table查找,没有就用metatable.__index"
__newindex(a, b, c) 对应表达式 a.b = c --> "重点5"
__call(a, ...) 对应表达式 a(...) --> "重点6"
++++++++++++++++++++"其他不常用[双下划线开头]"++++++++++++++++++++
1) __mod --> "取模" --> 'module'
2) __band --> "按位与" --> 'bit and'
3) __bor --> "按位或" --> 'bit or'
4) __bxor --> "按位异或"
5) __bnot --> "按位取反" --> 'bit not'
6) __shl --> "向左移位" --> 'shift left'
7) __shr --> "向右移位" --> 'shift right'
8) --idiv --> "floor除法"
(2)案例1讲解
+++++++++++++++++"加法操作"+++++++++++++++++
强调: a + b --> 'lua规则'必须调用 '元表'的__add(a,b)方法
备注:具体的实现'随意定义','不一定'就是数值运算.
table本身'不能'相加,但metatable可以改变'默认'行为
当lua"试图"对两个表进行'相加'时,先"检查"两者之一是否有元表,之后检查是否有一个叫"__add"的字段,若找到,则"调用"对应的值'元方法'
(3)案例2讲解
需求: a+b --> 实现"集合"的'并集'
1)代码
2)测试
3)结果
++++++++++++++"原理"++++++++++++++
1) 操作的两个'列表'至少有一个与'元表'关联,才能调用'元表的方法'
2) 把'被操作数'当作'元方法'的'参数'
4)查找规则
(4)库定义相关元方法
1)tostring
核心: tostring
函数"print"总是调用"tostring"来进行'格式化输出'
+++++++++++++++++"原理"+++++++++++++++++
1) 当对值进行'格式化'时,函数'tostring'会首先检查'值'是否有一个元方法"__tostring"
2) 如果'有',函数tostring就'调用这个元方法'来完成
3) 然后将'对象s1'作为参数'传递给该函数(__tostring)',然后把'元方法(__tostring)的返回值'作为函数tostring的返回值
2)pairs
从"lua5.2版本"开始,函数pairs也有了对应的'元方法',修改表'被遍历的方式'和为'非表的对象'增加遍历行为
备注: 当前这个地方不是"特别理解"
(5)表相关的元方法
1)__index元方法
补充: __index'不一定'必须是一个函数'function',还可以是一个表'table'
① 访问表中不存在的字段
背景:没有'__index'
'默认'行为:访问一个'不存在的字段'会得到nil
② 访问表中不存在的字段
背景: 有'__index' --> 作为'方法' --> 场景: "多继承、缓存"
原理: 没有找到'键',会引发'解释器'查找一个名为'__index'的元方法;'没有找到'这个元方法,结果就是'nil'
++++++++++++"等价方式[__index元方法是一个函数]"++++++++++++
原理: lua会以"表"和"不存在的键"为'参数'来调用该'__index'函数
③ 访问表中不存在的字段
背景: 有'__index' --> 作为'table'表,"非函数" --> "单继承"
++++++++++++"原理"++++++++++++
1) "首先"发现'age'不是table1的字段
2) "继续"查找'table1关联元表'的'__index'字段,发现字段的值是"表prototype"
3) "最后"在"表prototype"表中继续寻找-->等价'执行'prototype["gender"]
④ rawget
场景: rawget是为了"绕过__index"而出现的,类似让'__index'方法的"重写无效"
具体: 不考虑"元表的__index"情况,对表进行"原始"访问 --> 有就'打印',没有就'不打印'
语法: rawget(t,i) --> t是'原始table',i是'待访问'的元素
⑤ 小结
__index 用于'表的查询'
2)__newindex
场景: 表的"更新"
① 访问表中不存在的字段
背景: 有'__newindex',并且'__newindex'是一个'函数'
1、如果 __newindex 是一个'函数',在给 table '不存在的字段'赋值的时候,会'调用这个函数'
② 访问表中不存在的字段
背景: 有'__newindex',并且'__newindex'指向一个'表'
1.如果 __newindex 是一个'table',在给 table '不存在的字段'赋值的时候,会直接给 __newindex 的 'table' 赋值,而'不是原始的表'
③ rawset
作用:原始函数允许我们"绕过元方法",调用rawset(t,key,value)来'等价于't[key]=value,'不涉及'任何元方法
核点点:rawset与"元方法"没有任何关联
(6)其他元方法
① __call
1) __call元方法可以使'定义了他的表'可以'像函数'一样使用,可以'被调用','参数'是'另外一个表'
2) 调用:当'table名字'做为'函数名字'的形式"被调用"的时候,才会'调用__call函数'
3) 注意是'setmentable'的返回值来调用的
场景:后续'补充'
② __gc
Lua 执行'自动内存管理',这意味着您'不必担心'为新对象分配内存或在不再需要对象时释放它.Lua 通过运行'垃圾收集器'来收集所有死对象来自动管理内存
lua 元表中有个很好用的方法就是 "__gc",这个方法是在 table 被回收时'会触发的回调',可以用来做一些 lua'内存泄露' 及 '资源释放' 等操作
local tab = { _name = "default" }
setmetatable(tab, {
__gc = function ( t )
print("__gc, _name:", t._name)
end
})
collectgarbage("collect") -- '强制'垃圾回收
四 应用
(1)具有默认值的表
已知: 一个"普通表"中所有字段默认值都是'nil'
① 版本一
② 版本二
把'元表'的创建'抽离'出来
纠错: 不担心命名冲突,可以使用形如"__"这样的键作为'额外'的字段
③ 版本三
④ 版本四
对偶表示: '每个表'与其对'默认值'关联起来
具体表:一张独立的表,该表的"键"为'各中表',"值"为这些'各中表'的"默认值" -->弱引用表"[weak table]"
目的:保证'统一性',将各个table以及他们的默认值'保存'在一个公共的table中,不过这个table需要是weak table
+++++++++++++++++"特点"+++++++++++++++++
--1. 如果这个公共table是个'普通表'的话,那么作为key的table就会假设'永远存在',不会被lua回收
--2. 我们这个weak table的key'需要是weak',这样作为key的table如果'没有'被引用,会'被lua回收' --> 'weak table'
说明:讲解'垃圾回收'的时候'再讲解'
⑤ 版本五
(2)跟踪对表的访问
思考:'为什么'要做这样一个事情?
'实现思路'了解即可
① 跟踪单个表
② 跟踪多个表
(3)只读的表
+++++++++++++++++"实现原理"+++++++++++++++++
1) 由于在'修改'表元素的时候,会调用'__newindex'元方法
2) 所以可以在'该元方法中'自行'设计代码',进行'中断'操作,并给其'报错'提示
3) 代码层面'细细'体会'proxy'的作用 --> 当用户'修改'或'更新'时,'跟踪'所有的访问并将访问'重定向到'原来表的'合理元方法'
重点: '重定向'、重定向、重定向!