元表(类似操作符重载)

OpenResty 最佳实践

Lua 提供的所有操作符都可以被重载:

元方法 含义 “add” + 操作 “sub” - 操作 其行为类似于 “add” 操作 “mul” * 操作 其行为类似于 “add” 操作 “div” / 操作 其行为类似于 “add” 操作 “mod” % 操作 其行为类似于 “add” 操作 “pow” ^ (幂)操作 其行为类似于 “add” 操作 “unm” 一元 - 操作 “concat” .. (字符串连接)操作 “len” # 操作 “eq” == 操作 函数 getcomphandler 定义了 Lua 怎样选择一个处理器来作比较操作 仅在两个对象类型相同且有对应操作相同的元方法时才起效 “lt” < 操作 “le” <= 操作

除了操作符之外,如下元方法也可以被重载,下面会依次解释使用方法:

元方法 含义 “index” 取下标操作用于访问 table[key] “newindex” 赋值给指定下标 table[key] = value “tostring” 转换成字符串 “call” 当 Lua 调用一个值时调用 “mode” 用于弱表(week table) “metatable” 用于保护metatable不被访问

__index 元方法

下面的例子中,我们实现了在表中查找键不存在时转而在元表中查找该键的功能:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
mytable = setmetatable({key1 = "value1"},   --原始表
  {__index = function(self, key)            --重载函数
    if key == "key2" then
      return "metatablevalue"
    end
  end
})

print(mytable.key1,mytable.key2) –> output:value1 metatablevalue
关于 __index 元方法,有很多比较高阶的技巧,例如:__index 的元方法不需要非是一个函数,他也可以是一个表。
t = setmetatable({[1] = "hello"}, {__index = {[2] = "world"}})
print(t[1], t[2]) –>hello world

__call 元方法

__call 元方法的功能类似于 C++ 中的仿函数,使得普通的表也可以被调用。

1
2
3
4
5
6
7
8
9
functor = {}
function func1(self, arg)
  print ("called from", arg)
end

setmetatable(functor, {__call = func1})

functor("functor") –> called from functor
print(functor) –> output:0x00076fc8 (后面这串数字可能不一样)

面向对象编程


account.lua

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
local _M = {}

local mt = { __index = _M }

function _M.deposit (self, v)
self.balance = self.balance + v
end

function _M.withdraw (self, v)
if self.balance > v then
self.balance = self.balance - v
else
error("insufficient funds")
end
end

function _M.new (self, balance)
balance = balance or 0
return setmetatable({balance = balance}, mt)
end

return _M

引用代码示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
local account = require("account")

local a = account:new()
a:deposit(100)

local b = account:new()
b:deposit(50)

print(a.balance) –> output: 100
print(b.balance) –> output: 50

继承
判断为空

因此,我们要判断一个 table 是否为 {},不能采用 #table == 0 的方式来判断。可以用下面这样的方法来判断:

1
2
3
 function isTableEmpty(t)
    return t == nil or next(t) == nil
end