Lua 元表(Metatable)

413 阅读3分钟

Lua 元表(Metatable)

在 Lua table 中我们可以访问对应的 key 来得到 value 值,但是却无法对两个 table 进行操作(比如相加)。 因此 Lua 提供了元表(Metatable),允许我们改变 table 的行为,每个行为关联了对应的元方法。 例如,使用元表我们可以定义 Lua 如何计算两个 table 的相加操作 a+b。 当 Lua 试图对两个表进行相加时,先检查两者之一是否有元表,之后检查是否有一个叫 __add 的字段,若找到,则调用对应的值。 __add 等字段,其对应的值(往往是一个函数或是 table)就是"元方法"。 有两个很重要的函数来处理元表。

setmetatable(table,metatable)

对指定 table 设置元表(metatable),如果元表(metatable)中存在 __metatable 键值,setmetatable 会失败。

local table1 = { age = 12 }
local table2 = { name = '小明' }
table2.__metatable = '11'
setmetatable(table1, table2)
print(getmetatable(table1)) --11

getmetatable(table)

返回对象的元表(metatable)

__index 元方法

__index是:当我们访问一个表中的key不存在时,则会触发去寻找__index元方法,如果不存在,则返回nil,如果存在,则返回结果

table1 = { foo = 3 }
metaTable = {}
metaTable.__index = function()
    return '__index是:当我们访问一个表中的`key`不存在时,则会触发去寻找__index元方法,如果不存在,则返回`nil`,如果存在,则返回结果'
end
setmetatable(table1, metaTable) -- 对指定 table1 设置元表(metatable)
print('table1.foo=', table1.foo) --foo存在,则调用table1.foo
print('table1.attribute=', table1.attribute)--attribute不存在,则会调用__index元方法

微信图片_20220523142414.png

表元素查找规则

  1. 在表中查找,如果找到,返回该key对应的值,找不到则继续
  2. 判断该表是否有元表,如果没有元表,返回 nil,有元表则继续。
  3. 判断元表有没有 __index 方法,如果 __index 方法为 nil,则返回 nil,如果 __index 方法是一个表,则重复 1、2、3;

如果 __index 方法是一个函数,Lua会以key为参数调用该函数,并返回该函数的返回值。

如果我们希望在访问一个表时不调用__index元方法,那么可以使用函数rawget,它在不考虑元表的情况下对表进行简单的访问,定义为:rawget (table, key)

在不触发任何元方法的情况下 获取 table[key] 的值。 table 必须是一张表; key 可以是任何值。

local table1 = {}
local table2 = {}
local table3 = { aa = 10 }
table2.__index = table2
table3.__index = table3
setmetatable(table1, table2)
setmetatable(table2, table3)
print(table1.aa)

微信图片_20220523142517.png

__newindex元方法

当对表中不存在的key进行赋值时,如果这个表有__newindex元方法,则分两种情况:

__newindex指向table

local table1 = {bb=2}
local table2 = {}
setmetatable(table1, table2)
table2.__newindex = table2
table1.bb = 12
table2.aa = 10
print('table1.bb=', table1.bb)
print('table1.aa=', table1.aa)
print('table2.aa=', table2.aa)

微信图片_20220523144240.png

对查询表赋值时候 key存在的则对查询表key进行赋值

查询表不存在key则赋值给了__newindex指向的key进行赋值

__newindex指向函数

local table1 = { bb = 2 }
local table2 = {}
setmetatable(table1, table2)
table2.__newindex = function(table1, key, value)
    print(table1, key, value)
end
table1.bb = 11
table1.aa = 11
print('table1.aa=', table1.aa)
print('table1.bb=', table1.bb)

微信图片_20220523144838.png

给表创建新key时会调用这个函数

rawset (table, key, value)为原始表赋值

如果我们想为原始表进行赋值,可以使用rawset方法

rawset (table, key, value)
在不触发任何元方法的情况下将 table[key] 值设为 value。 table 必须是一张表, key 可以是 nil 与 NaN 之外的任何值。 value 可以是任何 Lua 值。

rawset(table1,"aa",456)
print(table1.aa)      -- 456