lua的__index和__newindex

lua的元表常常是初接触lua的人比较迷糊的一个设计,但元表的设计又赋予了lua强大的扩展能力。而在使用元表的过程中,__index和__newindex这两个索引是务必会遇到的,但这两个索引的功能跟lua本身简化的代码,往往让人丈二摸不清头脑。这里以我自己的理解来分享一下。


首先再介绍一下元表。从概念上来说,元表本身是一个表(table),表作为lua里的一个数据类型,其实与java里的map类似,就是key--value, key--value的一个个键值对的容器。lua的每个变量都可以为其设置一个元表,设置的方法是setmetatable(t, mt),即把mt设置为t的元表。比较典型的一种例子是t本身就是一个表的情况。在没有设置元表之前,t和mt都是表,它们样子上没什么差别,两者之间也没一毛钱关系。但设置以后,mt就相当于t的一个附加表,也就是通过t.key的方式访问某个key时,可以到元表mt里面去找(或者到某个指定的表里面去找),或者通过t.key的方式设置某个key的值时,可以写到mt里面去(或者写到某个指定的表里)。


而完成这样的功能,要经过一定的设置。怎么设置呢?这时就要用到索引__index和__newIndex了。


__index: 当访问表的某个索引时,如果该索引在表中不存在,则lua会在该表的元表(如果有的话)中查找__index索引,并在__index索引中指定的表或者函数中查找我们需要索引。这里面有点绕,因为元表虽说相当于一个附加表,但并不是直接在元表里查找,而是根据元表里的__index索引去查找。如果__index为空,那毫无疑问就是找不到了;如果__index的值是一个表,则在这个表里去查找我们所需要的索引;而如果__index的值是一个函数,当被它调用时,会把被访问的表和索引作为参数传入。这样的话我们可以根据不同的需要返回不同的值,例如增加条件逻辑等。


__index是表的例子:

other = { foo = 3 }
t = setmetatable({}, { __index = other })

结果:----------------------------------------------------
t.foo -- 3 ,现在__index包含的表{foo=3}查找
t.bar -- nil ,没找到

------------------------------------------------------------


__index是函数的例子:

other = {foo = 3}

t = setmetatable({},

{__index = function(t,key)

if key == "bar" then

return 1;

else

return rawget(t, key);

end

})

结果:------------------------------------------------------

t.foo -- 3 ,在other表里查找到
t.bar -- 1 ,直接根据__index指定的函数中返回

------------------------------------------------------------


与此类似,我们再来介绍下__newindex索引。

__newindex:当对表的某个索引赋值时,则lua会在该表的元表(如果有的话)中查找__newindex索引,并把索引和值设置到__newindex索引中指定的表中;如果__newindex指向的是函数,当被调用时会传递表、索引、值三个参数。同理,如果__newindex为空,那毫无疑问就是复制失败了;如果__newindex的值是一个表,则把索引和值设置到这个表里;而如果__newindex的值是一个函数,当被它调用时,会把被访问的表,索引和值作为参数传入。


__newindex是表的例子:

other = { }
t = setmetatable({}, { __index = other })


赋值:--------------------------------------------------

t.foo = 3

t.bar = 1

----------------------------------------------------------

结果:----------------------------------------------------
other = {foo=3, bar=1}

------------------------------------------------------------


__index是函数的例子:

other = {}

t = setmetatable({},

{__index = function(t,key, value)

if key == "bar" then

rawset(t, key , 2);

else

rawset(t, key , value);

end

})

赋值:--------------------------------------------------

t.foo = 3

t.bar = 1

----------------------------------------------------------


结果:------------------------------------------------------

other = {foo=3, bar=2}

------------------------------------------------------------