自己去查
Lua 最重要的两个结构及 table
与 function
,table
可以理解为 javascript
里的 {}
结构,但是缺没法定义一个有 思想
的属性。
public string today { get { return DateTime.Now.ToString(); } }
比如我们在 .NET
里可以通过 get
set
关键字定义访问器,又或者在 JS
里通过 defineProperty
来定义访问器。
Object.defineProperty(this,"today",{ get: ()=>new Date().toString()})
那么 Lua
里需要怎么定义呢?
通过元表 metatable
来实现,metatable
可以理解为具有特有属性的 table
下面罗列了部分 metatable
的私有属性。
prop | desc |
---|---|
index | 索引获取事件 |
newindex | 索引定义事件 |
call | 执行 {temp}() 时事件 |
len | 执行 #{temp} 时事件 |
元表 metatable
也是 table
那么它的私有属性也是可以重写的,我们通过 __{prop}
来重写元表的访问事件
{__len = function(t) return -1 end }
以上是一个重写元表获取长度的demo
local map={};
local o={
__index=function (t,k)
return k;
end
};
setmetatable(map, o);
print(map.getkeytest);
-- < getkeytest
map.getkeytest=nil;
print(map.getkeytest);
-- < getkeytest
map.getkeytest=16;
print(map.getkeytest);
-- < 16
在运行以上代码时你会发现一个问题,在 map
未设置 getkeytest
值时,它会正常的去访问 __index
索引器,但是在 getkeytest
赋值过后它响应的却是 16
,我们在来看看 __index
事件的文档,文档上没写。实际在访问 map
的时,当你访问的 key
未定义 或 值为空 时才会去触发元表的 __index
方法。
local map={ getkeytest=16 };
local o={
__index=function (t,k)
return k;
end,
__newindex=function (t,k,v )
print("__newindex",k)
end
};
setmetatable(map, o);
print(map.getkeytest);
-- < 16
map.getkeytest=18;
print(map.getkeytest);
-- < 18
map.getkeytest=nil;
map.getkeytest=19;
-- < __newindex getkeytest
print(map.getkeytest);
-- < getkeytest
map.getotherkeytest=20;
-- < __newindex getotherkeytest
print(map.getotherkeytest);
-- < getotherkeytest
我们在来试试 __newindex
同样,我们可以看出当 map
存在 getkeytest
时
getkeytest
不会触发 __indexgetkeytest
赋值,且值不为空时 不会触发 __newindexgetkeytest
赋值为 nil
时 不会触发 __newindexnil
的 getkeytest
赋值时 触发了 __newindexgetkeytest
触发了 __indexgetotherkeytest
赋值 触发了 __index在面向对象之前,我们在说下静态类和一般类的实现思路,我不喜欢 :
访问器,如果你还不知道 :
访问器,那么我觉得你可以不知道更好。
静态类就非常简单了很容易实现,如
local staic_class_demo={}
do
staic_class_demo.print=function(arg)
print("staic_class_demo",arg);
end
end
-- 如果是独立文件 需要返回 staic_class_demo
-- return staic_class_demo
staic_class_demo.print("你好")
-- < staic_class_demo 你好
接下来,面向对象
local new_obj = function(args)
local map, o, meta = {}, {}, {}
local list = {}
setmetatable(map, o)
o.__index = function(t, key)
if (meta[key]) then
if (meta[key].get) then
return meta[key].get()
else
return nil
end
else
return o[key]
end
end
o.__newindex = function(t, key, v)
if (meta[key]) then
if (meta[key].set) then
meta[key].set(v)
else
return
end
else
o[key] = v
end
end
map.add = function(a)
table.insert(list, a)
end
meta.length = {
get = function()
return #list
end
}
meta.name = {
get = function()
return args
end
}
return map;
end
local obj1=new_obj("obj1")
local obj2=new_obj("obj2")
obj1.add(1);
obj1.add(2);
obj2.add(3);
obj2.add(4);
obj2.add(5);
print(obj1.name,obj1.length)
-- < obj1 2
print(obj2.name,obj2.length)
-- < obj2 3
继续测试
obj1.name="test1";
obj2.name="test1";
print(obj1.name,obj1.length)
-- < obj1 2
print(obj2.name,obj2.length)
-- < obj2 3
这里需要注意的几个点是
o
只是用来定义 _index
及 _newindex
meta
来定义特殊字段,自定义访问器map
关联元表然后返回map
必须最后设置元表 setmetatable(map, o)
否则会死循环 stack overflow
了解了以上内容后我们可以封装一个快速定义自有属性的方法 Object.defineProperty
类似 JS
对象的定义属性
--
-- Object.lua
--
-- use for defineProperty
--
-- {
-- key = {
-- get = function()
-- --get function
-- end,
-- set = function(value)
-- -- set function
-- end
-- }
-- }
local Object = {_version = "1.1"}
do
Object.defineProperty = function(map, props)
local o = {}
setmetatable(map, o)
o.__index = function(t, k)
local tar = props[k]
if (tar) then
if (tar.value) then
return tar.value
elseif (tar.get) then
return tar.get()
else
return nil
end
else
return o[k]
end
end
o.__newindex = function(t, k, v)
local tar = props[k]
if (tar) then
if (tar.set) then
tar.set(v)
end
else
o[k] = v
end
end
end
-- end defineProperty
end
return Object
local new_obj = function(args)
local map, list = {}, {}
local like = nil
map.add = function(a)
table.insert(list, a)
end
-- 定义特殊属性 通过实现get/set来实现
Object.defineProperty(
map,
{
-- 定义长度属性,只读
length = {
get = function()
return #list
end
},
-- 定义名称属性,只读
name = {
get = function()
return args.."\t"..like
end
},
-- 定义喜好属性,可读可写
like={
get=function ()
return like
end,
set=function (v)
like=v
end
}
}
)
return map
end
local obj3=new_obj("obj3")
obj3.like="is me"
print(obj3.like)
-- < is me
print(obj3.name)
-- < obj3 is me
local RSS = {_Verison = 5.3, description = "类实现"}
do
local Object = require("object")
local strlen = function(str)
return #str
end
RSS.echo = function(...)
local text = table.concat({...}, "\t")
print(text)
end
RSS.new = function(name)
local map = {}
local age = 0
map.say = function()
RSS.echo(map.name, strlen(map.name), age)
end
Object.defineProperty(
map,
{
name = {value = name},
age = {
get = function()
return age
end,
set = function(v)
age = v
end
}
}
)
return map
end
end
-- return RSS
local jim = RSS.new("jim")
jim.name="jim2"
jim.age = 8
local jerry = RSS.new("jerry")
jerry.name="jerr2"
jerry.age = 24
jim.say()
-- < jim 3 8
jerry.say()
-- < jerry 5 24
RSS.echo("abc","bbc")
-- < abc bbc
扩展一个 echo 方法,需要 require("JSON")
local echo = function(...)
local temp = {}
for k, value in ipairs({...}) do
if ("table" == type(value)) then
table.insert(temp, json.encode(value))
else
table.insert(temp, value)
end
end
print(table.concat(temp, "\t"))
end
可以发现我们并没有常规方法来实现对象(你可以查阅参考文档来了解怎么使用常规方法定义对象),通过方法来定义对象的好处是整个语法都非常一致,用 .
来访问对象的方法及属性而不是 :
,然后在属性定义上也非常友好,不需要使用到 self
,不要去查阅什么是self,以上 lua
面向对象,完毕。