概念

元表其实很像面向对象中的继承,当给一张子表设置了一张元表时,该子表拥有元表的所有属性、方法。
任何表变量(table)都可以作为另一个表变量的元表。
任何表变量都可以有自己的元表(父表)。
当我们对子表或在子表中进行一些特殊的操作时,会执行元表中特定的内容。

操作

设置、获取元表

设置元表

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

  1. tab1 = {} -- 子表
  2. tab2 = {} -- 元表(父表)
  3. -- 设置元表
  4. -- 参数一:子表
  5. -- 参数二:元表(父表)
  6. setmetatable(tab1, tab2)

获取元表

getmetatable(table): 返回对象的元表(metatable)。

  1. tab1 = {} -- 子表
  2. tab2 = {} -- 元表(父表)
  3. -- 设置元表
  4. -- 参数一:子表
  5. -- 参数二:元表(父表)
  6. setmetatable(tab1, tab2)
  7. -- 获取元表
  8. tab3 = getmetatable(tab1)

特殊操作

__tostring() 方法

当子表被当做字符串使用,会触发元表中__toString()方法。

  1. tab1 = {} -- 子表
  2. tab2 = {
  3. -- tostring方法,接收一个默认参数:子表
  4. __tostring = function (t1)
  5. return "I am oyyh."
  6. end
  7. }
  8. setmetatable(tab1, tab2)
  9. -- 把子表当做字符串使用,此时会执行tab2中的__toString()方法,并输出: I am oyyh.
  10. print(tab1)

__call() 方法

当子表被当做函数使用时,会触发元表中__call()方法。

  1. -- 子表
  2. tab1 = {name = "table one"}
  3. -- 元表
  4. tab2 = {
  5. -- tostring方法,接收一个默认参数:子表
  6. __tostring = function (t)
  7. return t.name
  8. end, -- 注意这里有个逗号
  9. -- 第一个参数默认是子表
  10. -- 后面的参数,就是在子表被当做函数调用的传递过来的参数
  11. __call = function (t1, var1)
  12. print(t1, var1)
  13. end
  14. }
  15. setmetatable(tab1, tab2)
  16. -- tab1当做函数调用,并传递一个参数进去
  17. tab1(123)
  18. -- 此时会输出
  19. table one 123

__index 属性

当子表找不到某个元素时,会去__index指定的表中查找

  1. tab1 = {}
  2. tab2 = {
  3. age = 1,
  4. __index = {age = 2},
  5. -- __index = tab2 -- 不要这么写,这是一个坑,得到的会是个nil,如果要写,请在table外面写
  6. }
  7. -- 可以这么写
  8. -- tab2.__index = tab2
  9. setmetatable(tab1, tab2)
  10. print(tab1.age) -- 会输出2

__newindex 属性

当赋值时,如果赋值一个不存在的属性,那么会把这个值赋值到__newindex属性指向的表中,不会修改自己

  1. -- 先不设置__newindex属性,看看效果
  2. tab1 = {}
  3. tab2 = {}
  4. setmetatable(tab1, tab2)
  5. tab1.age = 1 -- 此时会在tab1中添加age属性
  6. print(tab1.age) -- 输出1
  7. -- 使用__newindex属性
  8. tab2.__newindex = {}
  9. -- 下面name属性将会被添加到__newindex指向的表中
  10. tab1.name = "oyyh"
  11. print(tab1.name) -- 会输出nil,如果想输出值,可以设置 tab2.__index = tab2.__newindex
  12. print(tab2.__newindex.name) -- 输出oyyh

运算符重载

当对子表进行运算符操作时,会触发元表中的指定方法:

    • : __sub
    • : __mul
  • / : __div
  • % : __mod
  • ^ : __pow
  • ..(拼接) : __concat
  • == : __eq
  • < : __lt
  • <= : __le

条件运算符,没有!=, >, >=,只能通过not取反,实际上也不需要,因为 t1 > t2 等价于 t2 < t1
对两个table使用条件运算符时,两个表的元素必须一致

  1. tab1 = {age = 1} -- 比较的第一个表
  2. tab2 = {age = 2} -- 比较的第二个表
  3. tab3 = {
  4. __eq = function (tab1, tab2)
  5. return true
  6. end
  7. }
  8. setmetatable(tab1, tab3)
  9. -- 比较两个表时,需要确保两个表的元素一致
  10. print(tab1 == tab2)