Lua基础(十四)元表和元方法

官方文档

一    Lua元表的理解

(1)首先理解JS的prototype

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

getmetatable(table): '返回'对象的元表(metatable)

  ②  setmetatable

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'

 lua的弱引用__mode

说明:讲解'垃圾回收'的时候'再讲解'

⑤    版本五

(2)跟踪对表的访问

思考:'为什么'要做这样一个事情?

'实现思路'了解即可

 ①    跟踪单个表

  

 ②    跟踪多个表

(3)只读的表

+++++++++++++++++"实现原理"+++++++++++++++++

 1)  由于在'修改'表元素的时候,会调用'__newindex'元方法

 2)  所以可以在'该元方法中'自行'设计代码',进行'中断'操作,并给其'报错'提示

 3)  代码层面'细细'体会'proxy'的作用 --> 当用户'修改'或'更新'时,'跟踪'所有的访问并将访问'重定向到'原来表的'合理元方法'

 重点: '重定向'、重定向、重定向!

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值