Lua基础知识精炼
Lua简介
Lua 是一门强大、高效、轻量级、可嵌入的脚本语言。其设计目标是成为一个能够轻松与其它语言(尤其是 C 和 C++)编写的代码集成的、易于使用的扩展语言。
核心特性
轻量级 | 体积非常小。完整的 Lua 解释器在经过压缩后只有几百 KB。编译速度快,运行开销低。 |
可嵌入 | 这是 Lua 最核心的设计目标。它可以非常方便地嵌入到应用程序(如游戏引擎、网络网关)中,作为该应用的脚本接口,为用户提供灵活的定制和扩展能力。 |
高效 | 拥有非常快的执行效率。其虚拟机(Lua VM)是最快的脚本语言虚拟机之一。 |
跨平台 | 由于 Lua 完全由 ANSI C 编写,它可以在所有主流操作系统(Windows, Linux, macOS, UNIX, Android, iOS 等)以及各种嵌入式系统和微控制器上运行。 |
补充
Lua是一门动态强类型语言。它的灵活性来自于类型的动态性(运行时确定),而它的安全性和严谨性来自于类型的强度(限制隐式转换)。这种结合使得Lua既灵活好用,又不容易产生因隐式转换导致的难以察觉的bug。
典例子:数字和字符串的拼接
local result = "Hello, I am " .. 25 -- 正确:数字 25 被显式地转换成了字符串 "25"
print(result) -- 输出: Hello, I am 25local result = 10 + "5" -- 错误!attempt to perform arithmetic (add) on number and string
第一行代码能成功,是因为字符串连接操作符 ..
显式要求其两边的操作数都必须是字符串。如果不是,Lua会调用tostring()
函数将它们转换为字符串。这是一种显式的规则,而不是随意的隐式转换。
第二行代码会抛出错误。算术操作符 +
期望两边都是数字,而Lua不会自动将字符串 "5"
转换成数字 5
来满足这个操作。你必须自己转换:10 + tonumber("5")
。
运行环境搭建
Sublime Text快速搭建Lua语言运行环境_sublime lua-CSDN博客
基本规范
1、Lua文件的后缀名是.Lua
2、变量
变量命名:Lua语言中的标识符是由任意字母、数字和下画线组成的字符串
注意:
(1)不能以数字开头
(2)“下画线 +大写字母”组成的标识符通常被lua语言用作特殊用途,应避 免使用。
变量类型:使用local修饰的变量是局部变量,未使用local修饰的变量是全局变量
3、打印
4、注释
5、分隔符;
在Lua中每个语句的分隔符";"不是必须的,可写可不写,根据自己的编码习惯决定即可。
6、if else
local a = 1
local b = 2----------第一种
if a == b thenend----------第二种
if a == b thenelse end----------第三种
if a == b thenelseif a < b thenelse end
数据类型
Lua语言有8种基本类型:
nil | boolean | number | string | table | function | thread | userdata |
空 | 布尔 | 数值 | 字符串 | 表 | 函数 | 线程 | 用户数据 |
nil 空
1、nil 类型表示无效值,它只有一个值 nil。
2、把nil赋值给一个全局变量时,相当于将该变量删除,Lua会回收该全局变量占用的内存。
boolean 布尔
boolean 类型只有两个可选值:true(真) 和 false(假)。但Lua 把 false 和 nil 都看作是 false,其他的都为 true(数字 0 和空字符串也是 true)。
number 数值
1、number 类型有两种内部表现方式, 整数 和 浮点数。
2、当需要区分整型值和浮点型值时,可以使用函数math.type
3、整数和浮点数的除法
string 字符串
字符串的声明
1、使用双引号
2、使用单引号
3、使用一对双方括号来声明长字符串/多行字符串。方括号括起来的内容可以包括很多行,并且内容中的转义序列不会被转义
获取字符串长度
字符串拼接
local str = "好好学习".."天天向上"
字符串反转
字符串转数值
字符串转数值,如果转换失败,会返回nil
变量转字符串
重复拼接字符串
截取字符串
也支持负数索引,-1代表最后一个字符,-2代表倒数第二个字符,以此类推
替换字符串
共有四个参数,第四个参数是设置替换的次数,如果不传则全部替换
转大写字母
转小写字母
查找子串
若找到,则返回子串的开始位置和结束位置;若未找到,则返回nil。
字符串格式化
--格式化一个整数 %d
local str1 = string.format("我的分数是:%d",25)--格式化一个浮点数 %f
local str2 = string.format("圆周率:%f",3.25)--格式化一个字符串 %s
local str3 = string.format("我的名字:%s","卡布奇洛")--添加占位符
local str4 = string.format("%04d",56)
table 表
1、表是Lua语言中最主要和强大的数据结构。可以使用表来表示数组、列表、字典等数据结构。2、表是不固定大小的,可以自动扩容,体现了它的灵活性。
3、当程序中不再有指向一个表的引用时,垃圾收集器会最终删除这个表并重用其占用的内存。
4、用表来实现列表时,是以1为初始索引。(C#是以0开始索引)
表的创建和初始化
表的索引
可以用任意类型的值作为表的索引,但这个值不能为nil
易混淆:
表的遍历
使用数值型for循环
遍历输出所有元素,即使遇到nil也输出
ipairs迭代器遍历
遇到nil会退出
pairs迭代器遍历
遇到nil会跳过,不会退出,继续打印输出后续元素
注意
当表表现字典时
ipairs只能遍历键为数值的字典,并且碰到
1、键为0
2、键为负数
3、值为nil
4、索引不连续
就会退出
pairs可以遍历键为任意数据类型的字典,可以遍历键为0、负数、索引不连续的情况不会退出,遇到值为nil的情况会跳过,输出的情况可能不是表中对应的顺序
表的操作
获取表的长度 | table.getn(tab) #tab |
连接表中元素 | 1、table.concat(tab),无缝拼接元素 2、table.concat(tab,s),以字符串s作为分隔符进行拼接 3、table.concat(tab,s,start),从start索引开始,以字符串s作为分隔符进行拼接 4、table.concat(tab,s,start,end)从start索引开始到end索引结束,以字符串作为分隔符进行拼接 |
向表中插入数据 | 1、table.insert(tab,value) 向表tab末尾插入value 2、table.insert(tab,pos,vlaue) 向表tab的pos索引位置插入value |
删除表中数据 | 1、table.remove(tab) 删除tab中最后一个元素 2、table.remove(tab,pos) 删除tab中pos位置的元素 上述都返回被删除的值 |
对表中元素进行排序 | --默认为升序 table.sort(tab) --降序 table.sort(tab,function(a,b) return a>b end) |
插入元素
local list = {1,2,3,4,5,6}--重载方法1 在列表末尾插入元素
table.insert(list,666)
--打印:1,2,3,4,5,6,666--重载方法2 在列表指定索引位置插入元素
--例子:在列表list的第3个索引位置处插入元素666
table.insert(list,3,666)
--打印:1,2,666,3,4,5,6
删除元素
local list = {1,2,3,4,5,6}--重载方法1 从列表末尾删除元素
table.remove(list)
--打印:1,2,3,4,5--重载方法2 删除列表指定索引位置的元素
table.remove(list,3)
--打印:1,2,4,5,6
拼接元素
是一个非常高效的方法,用于将数组表中的所有字符串元素连接成一个字符串。它比使用 ..
运算符进行多次连接要高效得多。
类似于C#中的string.Join方法
local list = {1,2,3,4,5,6}
local result
--重载方法1 无缝拼接元素
result = table.concat(list)
--打印:123456--重载方法2 用分隔符进行拼接
result = table.concat(list,",")
--打印:1,2,3,4,5,6--重载方法3 从start索引开始,用分隔符进行拼接
result = table.concat(list,",",3)
--打印:3,4,5,6--重载方法4 从start索引开始到end索引结束,用分隔符进行拼接
result = table.concat(list,",",2,5)
--打印:2,3,4,5
元素排序
-----------------单元素排序----------------------
local list = {3,1,5,2,4}--升序排序
--打印:1, 2, 3, 4, 5
table.sort(list)--降序排序
--打印:5, 4, 3, 2, 1
table.sort( list, function(a,b)return a > b
end)-----------------多条件排序----------------------
local list = {{id = 11,score = 606,num = 1},{id = 33,score = 505,num = 3},{id = 33,score = 505,num = 2},{id = 44,score = 303,num = 5},{id = 11,score = 505,num = 4},
}table.sort( list, function(a,b)if a.id ~= b.id thenreturn a.id < b.id endif a.score ~= b.score thenreturn a.score > b.score endreturn a.num > b.num
end)for k,v in ipairs(list) doprint(v.id,v.score,v.num)
end
参数打包
table.pack用于将可变数量的参数打包成一个表,并额外包含一个 n
字段来表示参数的数量。
-- 示例 1: 基本用法
local t1 = table.pack(1, 2, 3, "hello")
print(t1.n) -- 输出: 4
print(t1[1]) -- 输出: 1
print(t1[4]) -- 输出: hello-- 示例 2: 处理 nil 值
local t2 = table.pack(1, nil, 3, nil)
print(t2.n) -- 输出: 4
print(t2[2]) -- 输出: nil (不会被截断)-- 示例 3: 空参数
local t3 = table.pack()
print(t3.n) -- 输出: 0
实际应用:处理可变参数函数
function processArgs(...)local args = table.pack(...)print("参数个数:", args.n)for i = 1, args.n doprint("参数 " .. i .. ":", args[i])end
endprocessArgs("a", "b", "c")
-- 输出:
-- 参数个数: 3
-- 参数 1: a
-- 参数 2: b
-- 参数 3: c
参数解包
table.unpack用于将表中的元素展开为多个返回值。
-- 示例 1: 基本展开
local t = {10, 20, 30, 40}
local a, b, c = table.unpack(t)
print(a, b, c) -- 输出: 10 20 30-- 示例 2: 指定范围
local x, y, z = table.unpack(t, 2, 4)
print(x, y, z) -- 输出: 20 30 40-- 示例 3: 函数调用
local params = {"hello", "world", 123}
print(table.unpack(params)) -- 输出: hello world 123
与直接使用 ...
的区别
function test1(...)-- 直接使用 ...,无法知道参数个数(如果有 nil 会被截断)local args = {...}print("参数个数(可能不准确):", #args)
endfunction test2(...)-- 使用 table.pack,准确获取参数个数local args = table.pack(...)print("准确参数个数:", args.n)
endtest1(1, nil, 3) -- 输出: 参数个数(可能不准确): 1
test2(1, nil, 3) -- 输出: 准确参数个数: 3
function 函数
--形式1
function Test1()print("Hello World!1")
end--形式2
local Test2 = function()print("Hello World!2")
end
多返回值
可变长参数
--计算平均值
function average(...)result = 0local arg={...}for i,v in ipairs(arg) doresult = result + vendreturn result/#arg
endprint("平均值为",average(10,5,3,4,5,6))
3、点和冒号的区别:
点用于表直接调用表内部成员
冒号是将调用者作为第一个参数传入到函数中。使用self关键字就可以对调用者进行交互。
local Animal =
{name = "动物",height = 10,weight = 20,
}--self就是指代Animal表对象
function Animal:Test()print("动物的名字:",self.name)
endAnimal.Say = function ()print("动物叫声!")
endAnimal:Test()
Animal.Say()
thread 线程
方法 | 描述 |
coroutine.create(func) | 创建 coroutine,返回 coroutine一个线程, 参数是一个函数,当和 resume 配合使用的时候就唤醒函数调用 |
coroutine.resume() | 重启 coroutine,和 create 配合使用 |
coroutine.wrap(func) | 创建 coroutine,返回一个函数,一旦你调用这个函数,就进入 coroutine,和 create 功能重复 |
coroutine.status() | 查看 coroutine 的状态 |
coroutine.yield() | 挂起 coroutine,将 coroutine 设置为挂起状态,这个和 resume 配合使用能有很多有用的效果 |
coroutine.running() | 返回正在跑的 coroutine,一个 coroutine 就是一个线程,当使用running的时候,就是返回一个 corouting 的线程号 |
--使用coroutine.create()创建协程
co=coroutine.create(function ()print("协程")
end)
--协程的状态
print(coroutine.status(co))
--
coroutine.resume(co)--使用coroutine.wrap()创建协程
co1=coroutine.wrap(function()print("第二个协程")
end)co1()--协程挂起function Add()local a=0while(true)doa=a+1print(a)coroutine.yield(a)end
end--用create方法时,接收yield的第一个返回值是boolean
co2=coroutine.create(Add)
local x,y=coroutine.resume(co2)
print(x,y)
x,y=coroutine.resume(co2)
print(x,y)co3=coroutine.wrap(Add)
local m,n=co3()
print(m,n)
m,n=co3()
print(m,n)
循环控制
for循环
顺序遍历
local list = {1,2,3,4,5}--打印:1,2,3,4,5
for i = 1,#list doprint(list[i])
end
倒序遍历
local list = {1,2,3,4,5}--打印:5,4,3,2,1
for i = #list,1,-1 doprint(list[i])
end
间隔遍历
例如,间隔2个单位
local list = {1,2,3,4,5}--打印:1,3,5
for i = 1,#list,2 doprint(list[i])
end
while循环
先判断再运行
local x = 5while(x > 0)dox = x -1
end
repeat untile循环
先运行一次再判断
local x = 5repeatx = x - 1
until(x <= 0)
运算符
算术运算符
操作符 | 描述 |
+ | 加法 |
- | 减法 |
* | 乘法 |
/ | 除法 |
% | 取余 |
^ | 乘幂 |
- | 负号 |
关系运算符
操作符 | 描述 |
== | 等于 |
~= | 不等于 |
> | 大于 |
< | 小于 |
>= | 大于等于 |
<= | 小于等于 |
逻辑运算符
操作符 | 描述 | 实例 |
---|---|---|
and | 逻辑与操作符。 若 A 为 false,则返回 A,否则返回 B。 | (A and B) 为 false。 |
or | 逻辑或操作符。 若 A 为 true,则返回 A,否则返回 B。 | (A or B) 为 true。 |
not | 逻辑非操作符。与逻辑运算结果相反,如果条件为 true,逻辑非为 false。 | not(A and B) 为 true。 |
and、or、 not分别类似于C#中的 &&、||、!
and 和or遵循短路求值原则,即只在必要时才对第二个操作数进行求值。
and:如果第一个值为真则返回第二个值,如果第一个值为假则直接返回第一个值
or:如果第一个值为真则返回第一个值,如果第一个值为假则返回第二个值
实现三目运算符
local a = 1
local b = 2--打印较大值:2
local result = a > b and a or b
运算符优先级
从高到低的顺序
错误处理
error函数
功能:终止正在执行的函数,并返回message的内容作为错误信息
assert函数
assert首先检查第一个参数,若为true,assert不做任何事情;若为false,assert以第二个参数作为错误信息抛出。
数学函数
abs | 取绝对值 | math.abs(-15) | 15 |
acos | 反余弦函数 | math.acos(0.5) | 1.04719755 |
asin | 反正弦函数 | math.asin(0.5) | 0.52359877 |
atan2 | x / y的反正切值 | math.atan2(90.0, 45.0) | 1.10714871 |
atan | 反正切函数 | math.atan(0.5) | 0.463647609 |
ceil | 不小于x的最大整数 | math.ceil(5.8) | 6 |
cosh | 双曲线余弦函数 | math.cosh(0.5) | 1.276259652 |
cos | 余弦函数 | math.cos(0.5) | 0.87758256 |
deg | 弧度转角度 | math.deg(math.pi) | 180 |
exp | 计算以e为底x次方值 | math.exp(2) | 2.718281828 |
floor | 不大于x的最大整数 | math.floor(5.6) | 5 |
fmod (mod) | 取模运算 | math.mod(14, 5) | 4 |
frexp | 把双精度数val分解为数字部分(尾数)和以2为底的指数n,即val=x*2n | math.frexp(10.0) | 0.625 4 |
ldexp | 计算value * 2的n次方 | math.ldexp(10.0, 3) | 80 = 10 * (2 ^3) |
log10 | 计算以10为基数的对数 | math.log10(100) | 2 |
log | 计算一个数字的自然对数 | math.log(2.71) | 0.9969 |
max | 取得参数中最大值 | math.max(2.71, 100, -98, 23) | 100 |
min | 取得参数中最小值 | math.min(2.71, 100, -98, 23) | -98 |
modf | 把数分为整数和小数 | math.modf(15.98) | 15 98 |
pow | 得到x的y次方 | math.pow(2, 5) | 32 |
rad | 角度转弧度 | math.rad(180) | 3.14159265358 |
random | 获取随机数 | math.random(1, 100) | 获取1-100的随机数 |
randomseed | 设置随机数种子 | math.randomseed(os.time()) | 在使用math.random函数之前必须使用此函数设置随机数种子 |
sinh | 双曲线正弦函数 | math.sinh(0.5) | 0.5210953 |
sin | 正弦函数 | math.sin(math.rad(30)) | 0.5 |
sqrt | 开平方函数 | math.sqrt(16) | 4 |
tanh | 双曲线正切函数 | math.tanh(0.5) | 0.46211715 |
tan | 正切函数 | math.tan(0.5) | 0.5463024 |
举例:除数取整
随机数
math.random() | 返回一个在[0,1)范围内的伪随机小数 |
math.random(x) | 返回一个在[1,x]范围内的伪随机整数 |
math.random(x,y) | 返回一个在[x,y]范围内的伪随机整数 |
math.randomseed(x) | 1、math.randomseed(x)用于设置伪随机数发生器的种子,系统默认x为1 2、若x相同,则生成的伪随机数序列也相同,要想生成的随机数序列不同则将x设置伪不同的值 3、使用math.randomseed(os.time())来使用当前系统时间来初始化 |
--对随机性要求不高的情况
math.randomseed(os.time())
print(math.random(1,20))--对随机性要求较高的情况
local seed = tonumber(tostring(os.time()):reverse():sub(1,6))
math.randomseed(seed*1000)
local num = math.random(1, 20)--生成随机小数
function GetRandom(minValue,maxValue)local value = math.random()return minValue+value*(maxValue-minValue)
end --生成随机小数(限制位数)
function GetRandom(minValue,maxValue,decimalPlace)local value = math.random()local result = minValue+value*(maxValue-minValue)local format = "%."..decimalPlace.."f"result = string.format(format,result)result = tonumber(result)return result
end
日期和时间
os.time
获取当前时间戳
os.date
格式化为日期表
字段名 | 全称 | 含义 | 取值范围 | 示例 |
| Year | 年份(四位数字) | 通常 >= 1970 |
|
| Month | 月份 | 1 (一月) - 12 (十二月) |
(代表六月) |
| Day | 当月的第几天 | 1 - 31 |
|
| Year Day | 当年的第几天 | 1 - 366 |
(在非闰年中代表6月15日) |
| Week Day | 本周的第几天 | 1 (星期日) - 7 (星期六) |
(代表星期六) |
| Hour | 当前小时 (24小时制) | 0 - 23 |
(下午2点) |
| Minute | 当前分钟 | 0 - 59 |
|
| Second | 当前秒数 | 0 - 61 (允许闰秒) |
|
| Is Daylight Saving Time | 是否处于夏令时 |
( / ) |
(中国不实行夏令时) |
wday
的取值范围: 请注意,wday
是从 星期日(Sunday)开始 的。
1
= 星期日、2
= 星期一、3
= 星期二、4
= 星期三、5
= 星期四、6
= 星期五、7
= 星期六
生成当前时间的日期表
生成指定时间戳的日期表
格式化时间字符串
示例1:
local list =
{"%a","%A","%b","%B","%c","%H","%I","%j","%m","%M","%p","%S","%w","%W","%x","%X","%y","%Y"
}
for k,v in ipairs(list)doprint("指示符:",v,"打印:",os.date(v))
end
示例2:
local date = os.date("%Y/%m/%d %H:%M:%S")
模块和包
模块类似于一个封装库,可以把一些公用的代码放在一个模块文件中,以 API 接口的形式在其他地方调用,有利于代码的重用和降低代码耦合度。
_G表
_G是一个总表,他将我们声明的所有全局变量都存储在其中,局部变量不会存到该表中(加了local的变量)
for k,v in pairs(_G) doprint(k,v)
end
加载文件步骤
1、注册自定义文件加载路径
lua一般会从默认的路径 package.path 进行加载文件,但是有时候我们需要从自己指定的文件夹路径中加载。所以,我们需要在加载文件之前进行注册自定义文件加载路径
local custom_path =
{"C:\\Users\\24292\\Desktop\\LuaProject\\?.lua","C:\\Users\\24292\\Desktop\\LuaAnother\\?.lua",
}for k,v in pairs(custom_path) dopackage.path = package.path..";"..v
end
2、创建公共代码文件
C:\Users\24292\Desktop\LuaAnother\MyMath.lua
local MyMath = {}MyMath.Add = function(a,b)return a+b
endreturn MyMath
3、创建测试文件
C:\Users\24292\Desktop\LuaProject\Main.lua 使用require加载文件
local custom_path =
{"C:\\Users\\24292\\Desktop\\LuaProject\\?.lua","C:\\Users\\24292\\Desktop\\LuaAnother\\?.lua",
}for k,v in pairs(custom_path) dopackage.path = package.path..";"..v
endlocal MyMath = require("MyMath")
local result = MyMath.Add(1,2)
print(result)
require、dofile、loadfile的功能和区别
一般require比较常用,其他两个函数作了解
require
1、加载并执行指定的模块
2、对于搜索路径,不需要写文件的全路径和.lua扩展名。require
会根据 package.path
(用于 Lua 文件)和 package.cpath
(用于 C 库)中定义的模板路径来搜索文件。
3、加载后会将该模块缓存起来,多次加载时只会执行一次。这确保了模块的 Singleton(单例)特性,避免了重复定义和资源浪费。
dofile
1、编译并立即执行指定的 Lua 文件。
2、搜索路径需要写文件的全路径和.lua扩展名
2、加载后没有缓存,每次调用,文件都会被重新编译和执行。
loadfile
1、将指定的 Lua 文件编译成 chunk(一个代码块),并将其作为一个函数返回。它并不执行这个函数。执行该函数后,才能获取加载后返回的模块对象。
2、搜索路径需要写文件的全路径和.lua扩展名。
3、加载后没有缓存,每次调用,文件都会被重新编译和执行。
元表和元方法
元表(Metatable)和元方法(Metamethod)是Lua中实现面向对象编程、操作符重载和自定义行为的重要机制。
元表
元表是一个普通的Lua表,可以附加到另一个表上,用于定义或修改该表的行为。每个表都可以有自己的元表。
setmetatable(tab,metatab) | 将metatab设置为tab的元表 |
getmetatable(tab) | 获取表tab的元表 |
local t = {}
local mt = {} -- 元表-- 设置元表
setmetatable(t, mt)-- 获取元表
local mt_of_t = getmetatable(t)
元方法
元方法是定义在元表中的特殊键,当表参与特定操作时会被调用。
运算相关元方法
local Calculate = {}local data1 = {number = 2}
local data2 = {number = 4}setmetatable(data1,Calculate)
setmetatable(data2,Calculate)--加
Calculate.__add = function(a,b)return a.number+b.number
end
print(data1 + data2)--减
Calculate.__sub = function(a,b)return a.number-b.number
end
print(data1 - data2)--乘
Calculate.__mul = function(a,b)return a.number*b.number
end
print(data1 * data2)--除
Calculate.__div = function(a,b)return a.number/b.number
end
print(data1 / data2)--取余
Calculate.__mod = function(a,b)return a.number%b.number
end
print(data1 % data2)--等于判断
Calculate.__eq = function(a,b)return a.number == b.number
end
print(data1 == data2)--连接符
Calculate.__concat = function(a,b)return a.number .. b.number
end
print(data1..data2)--小于号
Calculate.__lt = function(a,b)return a.number < b.number
end
print(data1<data2)--小于或等于
Calculate.__le = function(a,b)return a.number <= b.number
end
print(data1 <= data2)--幂运算
Calculate.__pow = function(a,b)return a.number ^ b.number
end
print(data1 ^ data2)--负数
Calculate.__unm = function(a)return -a.number
endprint(-data1)
测试打印:
库定义相关元方法
__tostring
当要打印表名时,Lua就会查找该表的元表中的__tostring方法,并调用。
local animal = {name = "动物"
}local cat = {name = "小猫咪"
}setmetatable(cat,animal)animal.__tostring = function(t)print(t.name)return "动物方法"
endprint(cat)
__call
当表被当做一个函数被调用时,Lua就会查找该表的元表中的__call方法,并调用
local animal = {name = "动物"
}local cat = {name = "小猫咪"
}setmetatable(cat,animal)animal.__call = function(t)print("我是"..t.name.."的方法")
endcat()
表相关元方法
__index
当访问一个表中不存在的字段时,那么Lua就会寻找该table的metatable中的__index 键
元方法是一个表
local Human = {}
Human.__index = {score = 250
}
local Student = {}setmetatable(Student,Human)
print(Student.score)
元方法是一个函数
local Human = {}
Human.__index = function()print("调用index元方法")return 1000
end
local Student = {}
setmetatable(Student,Human)
print(Student.score)
__newindex
当给表中一个不存在的键赋值时,首先判断该表是否有元表,如果没有则相当于直接在表中声明一个变量并赋值,如果有则在元表中查找__newindex键,如果__newindex包含一个表则直接在该表中赋值,不在原始表中赋值。
__newindex 元方法用来对表更新,__index则用来对表访问 。
rawset
在不触发元方法__newindex的情况下,在原始表中进行声明赋值.
未用rawset情况:如果直接赋值的话,会查找tab2的元表中的元方法__newindex,将在元表tab1中声明该键并赋值,而不在tab2中声明赋值
使用rawset情况:当元表中有元方法__newindex时,使用rawset,给tab2中的不存在的键赋值,不会调用元方法__newindex,直接在tab2中声明赋值
rawget
在不触发元方法__index的情况下,直接在原始表中查找该字段
未用rawget情况:会查找元表中的元方法__index
local Human = {score = 100
}Human.__index = {score = 250
}local Student = {}
setmetatable(Student,Human)
print(rawget(Student,"score"))
使用rawset情况
local Human = {score = 100
}Human.__index = {score = 250
}local Student = {}
setmetatable(Student,Human)
print(rawget(Student,"score"))
垃圾收集
Lua 采用了自动内存管理。不用操心新创建的对象需要的内存如何分配出来, 也不用考虑在对象不再被使用后怎样释放它们所占用的内存。 Lua 运行了一个 垃圾收集器 来收集所有 死对象 (即在 Lua 中不可能再访问到的对象)来完成自动内存管理的工作。 Lua 中所有用到的内存,如:字符串、表、用户数据、函数、线程、 内部结构等,都服从自动管理。
Lua 提供了以下函数collectgarbage ([opt [, arg]])用来控制自动内存管理:
collectgarbage("collect") | 做一次完整的垃圾收集循环。 这是默认选项。 |
collectgarbage("count") | 以 K 字节数为单位返回 Lua 使用的总内存数。 这个值有小数部分,所以只需要乘上 1024 就能得到 Lua 使用的准确字节数(除非溢出)。 |
collectgarbage("restart") | 重启垃圾收集器的自动运行。 |
collectgarbage("stop") | 停止垃圾收集器的运行 |