|
|
|
这是 Lua 中最核心也最灵活的特性之一 —— 元表本质是给普通 table 附加 “自定义行为” 的特殊 table,比如让 table 支持加减运算、自定义索引查找规则等 - 元表(Metatable):一个特殊的 table,用来定义另一个 table(被元表修饰的 table 称为 “原表”)的特殊行为;
- 元方法(Metamethod):元表中以 __ 开头的键(比如 __add、__index),对应原表的自定义行为(比如 __add 定义加法运算);
- 核心函数:
- setmetatable(t, mt):给 table t 设置元表 mt(返回 t);
- getmetatable(t):获取 table t 的元表(返回元表或 nil)。
__index(索引查找)当访问原表中不存在的键时,Lua 会去元表的 __index 中查找,这是 Lua 实现继承、默认值的核心。 __index 可以是 table(优先查找该 table 的键),也可以是函数(自定义查找逻辑)。
Lua复制代码
-- 示例1:__index 为 table(继承的底层逻辑)local proto = {name = "默认名称", age = 18} -- 原型 tablelocal obj = {gender = "男"} -- 原表-- 给 obj 设置元表,__index 指向 protosetmetatable(obj, {__index = proto})-- 访问 obj 不存在的键,自动去 proto 找print(obj.name) -- 输出:默认名称(obj 无 name,从 proto 取)print(obj.age) -- 输出:18(同理)print(obj.gender)-- 输出:男(obj 自身有,直接取)-- 示例2:__index 为函数(自定义查找逻辑)local t = {}setmetatable(t, { __index = function(table, key) -- table=原表,key=访问的键 return "键 " .. key .. " 不存在,返回默认值" end})print(t.abc) -- 输出:键 abc 不存在,返回默认值__newindex(索引赋值)当给原表中不存在的键赋值时,Lua 会调用元表的 __newindex,而非直接给原表添加键。 - 用法和 __index 类似,可用于限制赋值、记录赋值日志、重定向赋值目标。
Lua复制代码
-- 示例:禁止给原表添加新键local t = {name = "张三"}setmetatable(t, { __newindex = function(table, key, value) print("赋值操作:表 =", table, "键 =", key, "值 =", value) -- 使用 rawset 绕开元方法,直接给原表赋值(避免递归触发 __newindex) rawset(table, key, value) end})t.age = 20 -- 报错:禁止给表添加新键:age(t 无 age 键,触发 __newindex)t.name = "李四" -- 正常执行(t 有 name 键,不触发 __newindex)__add/__sub/__mul 等通过元方法自定义原表的算术运算,比如让两个 table 支持加法。常用算术元方法:__add(+)、__sub(-)、__mul(*)、__div(/)、__eq(==)。
Lua复制代码
-- 示例:让 table 表示向量,支持加法运算local vec1 = {x = 1, y = 2}local vec2 = {x = 3, y = 4}-- 定义元表(包含 __add 元方法)local vec_mt = { __add = function(a, b) -- a=左操作数,b=右操作数 return {x = a.x + b.x, y = a.y + b.y} end}-- 给两个向量设置元表setmetatable(vec1, vec_mt)setmetatable(vec2, vec_mt)-- 向量加法(触发 __add)local vec3 = vec1 + vec2print(vec3.x, vec3.y) -- 输出:4 6__call(把 table 当作函数调用)当原表被当作函数调用时(比如 t()),触发 __call 元方法,可实现 “可调用的对象”。
Lua复制代码
local t = {name = "Lua"}setmetatable(t, { __call = function(table, ...) -- table=原表,...=调用时的参数 print("调用了 table,参数:", ...) print("table 的 name:", table.name) end})-- 把 table 当作函数调用(触发 __call)t(10, 20)-- 输出:-- 调用了 table,参数:10 20-- table 的 name:Lua__tostring(自定义打印格式)当用 print() 打印原表时,触发 __tostring 元方法,替代默认的 table: 0xXXXXXXX 格式。
Lua复制代码
local person = {name = "张三", age = 20}setmetatable(person, { __tostring = function(table) return "Person: " .. table.name .. "(" .. table.age .. "岁)" end})print(person) -- 输出:Person: 张三(20岁)(而非默认的内存地址)
|
|