很久之前接触过lua,最近在工作中又用到了一些,对lua这门语言又有了新的认识和理解。Lua是一门很简洁高效的语言,在openresty项目中大量使用,游戏开发中也使用lua来写一些脚本。在这里记录一下自己对lua中面向对象的理解。

table在lua中是一种kv的数据结构,也指由一些辅助函数组成的库。如:

local tb = {name="zhangsan", age=23} --此时变量tb为是一个table的数据结构
table.getn(tb) --此时便是调用了table库里的getn函数,获取tb的长度。

在lua中table数组的下标索引通常从1开始计数,因为这表示的是位置,而在c/c++中数组从0开始,表示的是偏移量。在初始化一个table数组时,若不显示的使用键值对赋值,则默认会用数字作为下标,从1开始。栗子:

local person = {name="zhangsan", 23, phone=138138138, addr="beijing"}
print(person["name"]) -- zhangsan
print(person[1]) -- 23

但一般不推荐上面这种混合的赋值方式。table中切记出现空洞,就是nil值被夹杂在非空值之间,这样在获取table长度的时候便会被截断。使得结果存在不可确定性。当删除table中的元素的时,不要用nil替换,建议使用remove来删除。
元表(metatable),其作用类似于c++中的操作符重载,通过定义metamethod来改变或增加table的行为。比如重定义__add操作,来实现两个table的并集。
Lua中任何一个table都有其metatable,任何一个table都可以是其他table的metatable,一组相关联的表,可以共享一个metatable。
例子:

local set1 = {20, 30, 40}
local set2 = {50, 60, 70}
local myadd = function(self, another)
    ...
end
setmetatable(set1, {__add = myadd}) -- 重载__add方法
local set3 = set1 + set2

重点说一下setmetatable,该函数为一个表设置元表,也可以这样来使用:

local set1 = setmetatable({20, 30, 40}, {__add = myadd})

当两个table相加时,lua会检查table的metatable中是否有__add,有的话,便会调用此metamethod。

模块与类

旧式的模块定义是通过module(“filename”, package.seeall)来显示声明一个包,这种方法将会返回一个由filename组成的table,并且还会定义一个包含该table的全局变量,这样在其他文件中也可以使用这个filename的全局变量。
现在比较推荐的定义方式是,定一个table,然后把需要的函数放到table中,然后返回这个table就可以了,其实这也是lua中面向对象的实现。Lua中不存在类,但Lua可以模仿类的概念。每个对象都有其元表,当调用不属于对象的某些操作时,会到自己的元表中查找。
如果我们有两个对象a和b:

setmetatable(a, {__index=b})

此操作就是让b作为a的元表。这样就可以认为,b是一个类,而a是实际的对象。
我们通过一个例子来说明。

--complex.lua文件
local complex =
{
    r = 0,
    i = 0
}
function complex:new(r, i)
    return setmetatable({r=r, i=i}, {__index = self} )
end
function complex:add(c1)
    self.r = self.r + c1.r
    self.i = self.i + c1.i
end
function complex:output()
    print(self.r, self.i)
end
return complex

–main.lua文件
local complex = require('complex')
local c = complex:new(1,2)
c:output() --1 2
local c2 = complex:new(2,3)
c:add(c2)
c:output() --3 5

当调用complex:new的时候,self其实就是指的complex,对象c当调用output的时候complex为其metatable,在对象c中找不到output,所以会从其metatable的__index对应的表中查找,故大致过程如下:

getmetatable(c).__index.output(c)

以上我们通过在complex中添加new、add、output等方法来模拟实现了类的概念。