这个系列主要学习Cocos2d-x Lua,总结Lua开发过程中所涉及的知识点,以及在开发过程中如何使用Cocos Code IDE。
本文介绍Lua中比较有意思的内容——_index元方法以及与_index有点相似的_newindex元方法。
一、强大的_index元方法
我们来想象一下,如果对一个table进行取值操作,但是table根本就没有这个值呢?比如:
local t = { name = "hehe", } print(t.money);
输出结果当然是:nil
t只用于name这个字段,而我们却访问了它的money字段,自然是返回nil了。
但是,如果我们不希望这样呢?我们希望在访问不存在的字段时,进行一些自定义的操作呢?没问题,Lua满足了我们,那就是_index元方法。
在使用加法操作时,会查找_add元方法,那么,在调用table不存在的字段时,会调用_index元方法,这是一样的规则。
我们来看看代码:
local t = { name = "hehe", } local mt = { __index = function(table, key) print("虽然你调用了我不存在的字段,不过没关系,我能探测出来:" .. key); end } setmetatable(t,mt); print(t.money);
我们给table设置了一个自定义的元表,元表的_index元方法使用了我们的函数。
运行结果如下:
[LUA-print] 虽然你调用了我不存在的字段,不过没关系,我能探测出来:money
[LUA-print] nil
当调用了不存在的money字段时,就会调用table元表里的_index元方法,并且会传递table和字段名两个参数。于是,我们就可以在这个函数里做很多自定义的操作了。
1. 继承的实现方法
虽然现在还没到讲解继承的时候,不过,我们可以先来稍微品尝一下。当调用table中不存在的字段时,会调用table元表的__index元方法,这个刚刚我们已经说过了。但是,如果这个__index元方法是一个table的话,那么,就会在这个table里查找字段,并调用。
说起来,有点混乱,看代码就清楚了:
local t = { name = "hehe", } local mt = { __index = { money = "900,0000", } } setmetatable(t,mt); print(t.money);
留意__index,我们给它赋值了一个table,这个table中有一个money对象。那么,当调用t的某个不存在的字段时,就会去查找__index里的table,如果找到这个字段,就调用它。
输出结果如下:
[LUA-print] 900,0000
2. 试试继承
刚刚的例子还没法体会到“继承”的概念,我们再来看一个例子:
local smartMan = { name = "none", age = 25, money = 9000000, sayHello = function() print("大家好,我是聪明的豪。"); end } local t1 = {}; local t2 = {} local mt = {__index = smartMan} setmetatable(t1, mt); setmetatable(t2, mt); print(t1.money); t2.sayHello();
我们定义了一个table,叫做smartMan,作为“基类”。然后新建两个table,t1和t2,将smartMan作为元表的__index元方法。
于是,当调用t1、t2的money或者sayHello字段时,实际上就会找到smartMan的字段。是不是很像继承的样子?
二、也很强大的_newindex元方法
1. 查询与更新
上一节中,我们介绍了_index元方法,总结来说,_index元方法是用于处理调用table中不存在的字段。注意,【调用】这个词,只是调用,而不是赋值。
如果,我们要对table中某个不存在的字段赋值呢?没错,我们直接就能赋值了,不会报错的。问题是,如果我想监控这个操作呢?如果有人想对table不存在的字段进行赋值的时候,我想进行一些额外的处理呢?
这时候就要用到_newindex。
大家要记住这句话:_index用于查询,_newindex用于更新。等会不要混乱了, 初次接触的话,有可能会混乱。
2. 看看普通的赋值情况
我们先来看看正常情况下的赋值,如代码:
local smartMan = { name = "none", money = 9000000, sayHello = function() print("大家好,我是聪明的豪。"); end } local t1 = {}; local mt = { __index = smartMan, } setmetatable(t1, mt); t1.sayHello = function() print("en"); end; t1.sayHello();
这是上一节用过的例子,一个模仿继承结构的例子。
来分析一下,mt作为t1的元表,设置_index为smartMan。于是,当我们调用t1中不存在的字段时,就会自动去smartMan中查找。比如我们调用了t1.sayHello(),自然能找到对应的函数。
先来看看输出结果:
[LUA-print] en
我们调用t1的sayHello字段,t1并不存在这个字段(虽然可以通过_index的方式来找到smartMan的sayHello字段)。但这不影响,给这个字段赋值,然后再调用t1.sayHello(),发现是成功的。
这和我们以往的做法一样,对table做正常的赋值操作,不管table本身是否存在这个字段。
3. 监控赋值
好了,普通情况我们已经试过了,如果我们想监控table的赋值操作呢?对于不存在的字段,我们不需要被赋值呢?想要制作一个只读的table呢?
那么,如果你有这些想法,请看看下面的代码:
local smartMan = { name = "none", money = 9000000, sayHello = function() print("大家好,我是聪明的豪。"); end } local t1 = {}; local mt = { __index = smartMan, __newindex = function(table, key, value) print(key .. "字段是不存在的,不要试图给它赋值!"); end } setmetatable(t1, mt); t1.sayHello = function() print("en"); end; t1.sayHello();
留意mt元表,我们给它加了一个__newindex。
运行代码,输出结果如下:
[LUA-print] sayHello字段是不存在的,不要试图给它赋值!
[LUA-print] 大家好,我是聪明的豪。
很显然,sayHello字段赋值失败,因为给sayHello字段赋值的时候,调用了_newindex元方法,代替了赋值操作。
这里有一个地方要注意的,t1中确实是不存在sayHello字段的,它只是因为有元表存在,而元表里的_index元方法的值是smartMan这个table。
从而,可以在t1找不到sayHello字段的时候,去smartMan中寻找。但实际上,t1确实是不存在sayHello字段的,不知道大家能绕明白不?因此,当试图给t1的sayHello字段赋值时,Lua判定sayHello字段是不存在的,所以会去调用元表里的_newindex元方法。
_newindex元方法被调用的时候会传入3个参数:table本身、字段名、想要赋予的值。
4. 隔山打牛,通过给一个table给另一个table的字段赋值
和__index一样,__newindex元方法也可以赋予一个table值。这种情况下就有点意思了,先看看代码:
local smartMan = { name = "none", } local other = { name = "大家好,我是很无辜的table" } local t1 = {}; local mt = { __index = smartMan, __newindex = other } setmetatable(t1, mt); print("other的名字,赋值前:" .. other.name); t1.name = "小偷"; print("other的名字,赋值后:" .. other.name); print("t1的名字:" .. t1.name);
这次的代码和刚刚差不多,但是我们新加了一个other的table,然后把other作为_newindex的值。于是,当给t1的name字段赋值时,就会发生一些奇怪的事情…
先来看看输出结果:
[LUA-print] other的名字,赋值前:大家好,我是很无辜的table
[LUA-print] other的名字,赋值后:小偷
[LUA-print] t1的名字:none
当给t1的name字段赋值后,other的name字段反而被赋值了,而t1的name字段仍然没有发生变化。(实际上t1的name字段还是不存在的,它只是通过__index找到了smartMan的name字段,这个就不唠叨了。)
于是,我们给t1的name赋值的时候,实际上是给other的name赋值了。好吧,可怜的other。
5. 总结规则
这就是_newindex的规则:
a.如果_newindex是一个函数,则在给table不存在的字段赋值时,会调用这个函数。
b.如果_newindex是一个table,则在给table不存在的字段赋值时,会直接给_newindex的table赋值。