lua中每一个值都有一套预约义的操做集合,好比数字是能够相加的,字符串是能够链接的,可是对于两个table类型,则不能直接进行“+”操做。这须要咱们进行一些操做。在lua中有一个元表(metatable),咱们能够经过元表来改变一个值的行为,使其在面对一个非预约义的操做时执行一个指定的操做。好比,如今有两个table类型的变量a和b,咱们能够经过metatable定义如何计算表达式a+b,具体的在Lua中是按照如下步骤进行的:函数
1.先判断a和b二者之一是否有元表
lua
2.检查该元表中是否有一个叫__add的字段
spa
3.若是找到该字段,就调用该字段对应的值,这个值对应的是一个metamethod(元方法)
prototype
4.调用__add对应的元方法计算a和b的值
code
在table中,咱们能够从新定义的元方法有如下几个:
索引
__add(a, b) --加法ip
__sub(a, b) --减法字符串
__mul(a, b) --乘法原型
__div(a, b) --除法string
__mod(a, b) --取模
__pow(a, b) --乘幂
__unm(a) --相反数
__concat(a, b) --链接
__len(a) --长度
__eq(a, b) --相等
__lt(a, b) --小于
__le(a, b) --小于等于
__index(a, b) --索引查询
__newindex(a, b, c) --索引更新(PS:不懂的话,后面会有讲)
__call(a, ...) --执行方法调用
__tostring(a) --字符串输出
__metatable --保护元表
下面来进行一些个别的说明:
1.__add
local mytable = setmetatable({1,2,3},{ __add = function(mytable,newtable) for i=1,#newtable do table.insert(mytable,#mytable+1,newtable[i]) end return mytable end }) local secondtable = {4,5,6} mytable = mytable + secondtable for k,v in ipairs(mytable) do print(k,v) end
输出为:
1 1 2 2 3 3 4 4 5 5 6 6
注:因为mytable和secondtable都是table,因此它们不能简单的相加。这个时候,lua会去找mytable和secondtable,去看它们有没有__add方法,这时候,lua发现mytable中有__add,所以调用了__add对应的函数,__add对应的函数传入的第一个参数是mytable,第二个参数是secondtable。实际上就是至关于把secondtable的值都附加到mytable中。
如今你们有没有想过,若是两个table同时都有__add方法,那lua会执行哪一个呢?咱们看一下下面的代码:
local mytable = setmetatable({1,2,3},{ __add = function(mytable,newtable) print("这是第一个table") for i=1,#newtable do table.insert(mytable,#mytable+1,newtable[i]) end return mytable end }) local secondtable = setmetatable({4,5,6},{ __add = function(mytable,newtable) print("这是第二个table") for i=1,#newtable do table.insert(mytable,#mytable+1,newtable[i]) end return mytable end }) mytable = secondtable + mytable + secondtable + mytable for k,v in ipairs(mytable) do print(k,v) end
输出为:
这是第二个table 这是第二个table 这是第二个table 1 4 2 5 3 6 4 1 5 2 6 3 7 4 8 5 9 6 10 1 11 2 12 3 13 1 14 2 15 3
注:由输出咱们能够知道,哪一个table在“+”操做最前面,则执行该table的__add方法,并且该语句后面也是都是调用第一个table的__add方法。还有朋友可能不太理解为何会输出1-15,不该该是1-12吗?实际上是这样的咱们在secondtable的__add方法已经把secondtable的值就行改变了,当他执行完secondtable + mytable这个的时候,它已经变成了4,5,6,1,2,3而mytable是在secondtable + mytable + secondtable+ mytable 执行完了以后才被赋值的,因此在右边的两个mytable都是1,2,3
接着咱们在看一下__index元方法,当访问一个table中不存在的字段时,获得的结果为nil。这是对的,但并不是彻底正确。实际上,这些访问会促使解释器去查找一个叫__index的元方法。若是没有这个元方法,那么访问结果如前述的胃nil。不然,就由这个元方法来提供最终结果。看下面的例子:
window = {} window.prototype = {x=0,y=0,width=100,height=100} window.mt = {} function window.new(o) setmetatable(o,window.mt) return o end window.mt.__index = function(table,key) print("-------------------",key) for k,v in pairs(table) do print(k,v) end return window.prototype[key] end w = window.new{x=10,y=20} print(w.width,w.x,w.y,w.height)
输出:
------------------- width y 20 x 10 ------------------- height y 20 x 10 100 10 20 100
注:lua检测到w中没有某字段,但在其元表中却有一个__index字段,那么lua就会以w(table)和"width"(不存在的key)来钓鱼这个__index元方法。随后元方法用这个key来索引原型table,并返回结果。__index元方法不必定是一个函数,它还能够是一个table,当它是一个函数时,lua以table和不存在的key做为参数来调用该函数,而当它是一个table时,lua就以相同的方式来从新访问这个table(会在这个table中继续查找)。
咱们如今来看一下__newindex元方法:
__newindex元方法于__index相似,__newindex用于table的更新,而__index用于table的查询。当对一个table不存在的索引赋值时,解释器就会查找__newindex元方法。若是有这个元方法,解释器就调用它,而不是执行赋值。若是这个元方法是一个table,解释器就在该table中执行赋值,而不是对原来的table。看下面的例子:
local man = { name = "大神", money = 30000, play = function() print("我去打篮球") end } local t = {} local mt = { __index = man, __newindex = function(table,key,value) print(key .. "不存在,不要尝试给它赋值") end } setmetatable(t,mt) t.play = function() print("我去踢足球") end t.play()
输出:
play不存在,不要尝试给它赋值 我去打篮球
注:由输出咱们能够知道t.play = function() print("hi") end并无起到做用,这是由于给t的sayhello字段赋值的时候,lua判断play字段不存在,因此会去调用元表里的__newindex元方法。__newindex元方法被调用的时候会传入3个参数:table自己,字段名,想要赋予的值。
咱们再看下面的例子:
local man = { name = "程序猿" } local updatetable = { name = "我是美男子" } local t = {} local mt = { __index = man, __newindex = updatetable } setmetatable(t,mt) print("updatetable赋值前:",updatetable.name) t.name = "我是大帅哥" print("updatetable赋值后:",updatetable.name) print("t.name:",t.name)
输出:
updatetable赋值前: 我是美男子 updatetable赋值后: 我是大帅哥 t.name: 程序猿
这就是上面所说的:若是这个元方法是一个table,解释器就在该table中执行赋值,而不是对原来的table。看下面的例子。
若是还不明白,在看下面的例子可能就会更加理解了:
mymetatable = {} mytable = setmetatable({key = "value"},{__newindex=mymetatable}) print(mytable.key) mytable.newkey = "newkey value" print(mytable.newkey,mymetatable.newkey) mytable.key = "key value" print(mytable.key,mymetatable.key)
输出:
value nil newkey value key value nil
注:当lua执行mytable.newkey = "newkey value"这句时,会去mytable中查找newkey,发现没有newkey的时候就会去找__newindex这个元方法,而后对__newindex对应的table进行赋值,所以mytable.newkey为nil,mymetatable.newkey为newkey value。而执行mytable.key = "key value"语句的时候,mytable存在key,所以只修改mytable中的key对应的值。
咱们再来看一下__call这个元方法,这个元方法会在Lua 调用一个值时调用,看下面的例子:
mytable = setmetatable({10},{ __call = function(mytable,newtable) sum = 0 for i=1,#mytable do sum = sum + mytable[i] end for i=1,#newtable do sum = sum + newtable[i] end return sum end }) newtable = {10,20,30} print(mytable(newtable))
输出:
70
注:mytable是一个table,当调用其时(mytable(newtable))会执行__call这个元方法,第一个参数为mytable,第二个参数为newtable。
__tostring元方法,它主要是更改打印语句,看下面的例子:
mytable = setmetatable({},{ __tostring = function(mytable) return "我改变你的值" end }) print(mytable)
输出:
我改变你的值