元表的作用在于对于两个表之间的操作,改变table的行为。
1.设置/获取元表
mytable={}
mymetatable={}
mytable=setmetatable(mytable,mymetatable)
将mymetatable设置为mytable的元表。返回值为普通表
mymetatable=getmetatable(mytable)
获取mytable的元表,返回值为元表
2.__index元方法
当通过键值访问table时,如果这个键没有值,并且该table存在元表的情况下,会去查找元表中的__index键。如果__index存的是一个方法,就会去执行该方法中的内容。如果是一个表,会访问表中对应键值的值。具体操作流程为:
例子:
mytable={key1="c#",key2="lua"}
mymetatable={key3="java"}
setmetatable(mytable,mymetatable)
mymetatable.__index=function(table,key)
return "javaScript"
end
print(mytable["key3"])
print(mytable["key4"])
执行以上代码,打印出的结果都是javaScript。虽然mymetatable中本身有key3的值,但访问mytable时,不会去访问mymetatable中原有的键值,当发现table中不存在该键时,才会去访问元表中是否有__index,这里__index中是一个方法,所以会执行方法内容。
同样,如果把上面mymetatable.__index换成一个表,就会去访问该表中的键值元素。例mymetatable.__index={key4="c++"},这时返回的key4就成了c++.
3.__newindex
当对table进行赋值操作时,如果有元表,会执行__newindex操作.和index不同之处在于,一个对表的读取,一个对表的赋值。相同处在于都是在普通表中没有该键值时,新键值中的内容都可以是方法或者一个表。
比如上面的方法:
mytable={key1="c#",key2="lua"}
mymetatable={key3="java"}
setmetatable(mytable,mymetatable)
mymetatable.__newindex=function(table,key,value)
print(table,key,value)
end
mytable.key5="c++"
print(mytable.key5)
这里如果没有元表,会直接对mytable赋值,打印出来的是c++.但有元表后会执行__newindex中的方法,不会进行赋值操作,所以打印出来为nil.这里方法和index不同点在于有3个参数,我们可以通过
rawset(tab,key,value)---------lua内置方法,对表赋值
lua中的这个内置函数,对参数进行赋值,效果和mytable.key5="c++"一样。
上面对__newindex是一个方法,如果是一个表的话,会将新的键和值赋给该表,相当于第三个表,这个键值不属于mytable,也不属于它的元表。例:
newtable={}
mymetatable.__newindex=newtable
mytable.key5="c++"
print(mytable.key5,mymetatable.key5,newtable.key5)
得到的结果是只有newtable.key5是c++.当然这里mymetatable.__newindex=mymetatable也是可以的。其实原理就是:
当给table赋值时,如果该键值本身存在,会直接改变原键值元素。如果不存在,会访问元表的_newindex,如果__newindex是一个方法,执行方法。如果是一个表,会将新的键值赋给这个表。
4.__add元方法
lua本身没有对两个表进行合并的方法,直接写table1+table2会报错。可以通过元表中__add键重写操作。下面是例子:
table1={"java","lua","c#"}
table2={"c++","php"}
setmetatable(table1,{
__add=function(tab1,tab2)
mi=#tab1
for k,v in pairs(tab2) do
mi=mi+1
--tab1[mi]=v
table.insert(tab1,mi,v)
end
end
})
table3=table1+table2
for k,v in ipairs(table1) do
print(k,v)
end
这里相当于重写了+的操作,最终得到的结果是table1变成了:
1 java
2 lua
3 c#
4 c++
5 php
这里要注意的一点是,两个相加的表只要其中有一个在元表中定义了add操作,就可以使用+来进行合并表。这里table1+table2改变的是table1,如果改为table2+table1,变的就是table2了。
5__call元方法
__call元方法相当于把表当方法来使用。如果不在元表中对__call键值进行处理的话,是不能直接在表后面加()进行处理的。举个例子,让表中所有元素值加一。例:
mytable={1,2,3,4,5}
setmetatable(mytable,{
__call= function(tab,arg)
for k,v in pairs(tab) do
tab[k]=v+arg
print(tab[k])
end
end
})
mytable(2)
输出的结果是3,4,5,6,7
这里__call中第一个参数是要处理的表,第二个参数才是调用时的第一个参数。
6__tostring元方法
__tostring元方法用于修改表的输出行为,比如这里输出一个表中所有元素的和。
mytable={1,2,3,4,5}
setmetatable(mytable,{
__tostring=function(tab)
local num=0
for k,v in pairs(tab) do
num=num+v
end
return num
end
})
print(mytable)
这里输出的值将不再是表对应的地址,而是这个表中所有元素的和。记得此处既然要输出,要返回一个值。