19.Lua语法知识总结
发布时间 : 2024-04-03 13:19
字数:6k
阅读 :
19.总结
19.1 核心要点速览 分组、注释和打印
功能
语法/示例
说明
单行注释
-- 注释内容
--[=[
可临时关闭注释
多行注释
--[[ 多行注释内容 ]]
嵌套注释需用 --[=[ … ]=]
打印输出
print("Hello Lua")
自动拼接参数(如 print(1, "a")
输出 1 a
)
变量类型 Lua 共有 8 种变量类型 ,均为动态类型(赋值时自动推导,无需显式声明):
类型
核心特点 & 示例
特殊说明
nil
唯一值 nil
,标识“空” 示例:local a
(默认 nil
)、a = nil
(释放变量)
未赋值变量默认是 nil
;给变量赋 nil
可回收其内存
boolean
仅 true
、false
两个值 示例:local flag = true
真假规则 :仅 nil
和 false
为“假”,其余(如 0
、空字符串 ""
、空表 {}
)均为“真”
number
统一数值类型(整数 + 浮点数,内部以双精度浮点数存储,Lua5.3+ 支持整数区分) 示例:local num = 10
(整数)、local pi = 3.14
(浮点数)
语法无整数/浮点数区分,运算自动适配(如 5/2=2.5
,5//2=2
是整除)
string
字符串,用引号("abc"
、'abc'
)或长字符串语法 [[ 多行内容 ]]
定义 示例:local str = "Hello"
、local text = [[Lua 多行]]
不可变(修改需重新生成);拼接用 ..
(如 "Hi" .. "Lua"
);长度用 #
(如 #"abc"=3
)
function
函数,支持匿名函数、闭包 示例:function add(a,b) return a+b end
(命名函数)local f = function() print("匿名") end
(匿名函数)
可作为参数/返回值(高阶函数);闭包可捕获外部变量(如 local x=1; function f() x=x+1 end
)
table
核心复合类型,兼具数组 (顺序结构)和字典 (键值对)功能 示例:local arr = {1,2,3}
(数组,索引从 1
开始)local dict = {name="Lua", ver=5.4}
(字典,通过 dict.name
访问)
动态扩容,无固定长度;是 Lua 唯一的复合数据结构,需用 table
库操作(如 table.insert
插入)
userdata
自定义用户数据(用于 C/C++ 扩展),存储外部语言(如 C)的内存数据 示例:(需结合 Lua C API 实现,纯 Lua 代码无直接示例)
需通过 C 扩展创建,Lua 脚本中仅作为“黑盒”对象使用(如游戏引擎的自定义资源对象)
thread
协同程序(coroutine
),非系统线程,是协作式多任务 的载体 示例:local co = coroutine.create(function() print("协程") end)
通过 coroutine.resume
启动,coroutine.yield
暂停;同一时间仅一个协程运行,实现“伪并发”
基础类型(nil
、boolean
、number
、string
)是“值类型”,赋值时拷贝内容;
复杂类型(function
、table
、userdata
、thread
)是“引用类型”,赋值时传递引用(修改会影响原对象)。
字符串 字符串基础(声明、长度、多行)
功能
语法/示例
关键说明
声明方式
str1 = "双引号"
str2 = '单引号'
str3 = [[长字符串(支持换行)]]
双引号/单引号功能一致;[[...]]
可多行,无需转义
长度计算(#)
print(#"aBcdEfG字符串") → 16
(汉字占3字节,UTF-8编码)
#
统计字节数 (非字符数),如”中”占3字节→#"中"=3
多行打印
print("123\n456")
(转义符\n
)str = [[我是\n韬]]
(长字符串保留换行)
转义符\n
显式换行;[[...]]
直接保留换行格式
字符串拼接
方式
语法/示例
特点说明
运算符 ..
print("123" .. "456") → 123456
str1=111; print(str1 .. 222) → 111222
自动拼接,支持非字符串(自动转字符串)
string.format
print(string.format("我今年%d岁", 18)) → 我今年18岁
占位符拼接:%d
(数字)、%s
(字符串)、%a
(任意)
类型转换(其他类型→字符串)
方法
语法/示例
说明
tostring
a=true; print(tostring(a)) → "true"
num=123; print(tostring(num))
强制转换任意类型为字符串(nil
转”nil”)
字符串库函数(string.xxx
)
方法名
语法/示例
关键说明
注意事项
转大写 upper
string.upper("abC") → "ABC"
所有字符转大写(仅ASCII字母有效)
不修改原字符串
转小写 lower
string.lower("AbC") → "abc"
所有字符转小写(仅ASCII字母有效)
同上
翻转 reverse
string.reverse("abc") → "cba"
反转字符串字节顺序(汉字会乱码,因UTF-8多字节)
索引从1开始(Lua通用规则)
查找 find
string.find("abCdef", "Cde") → 3 5
(返回起始、结束索引)
查找子串位置,无匹配返回nil
区分大小写
截取 sub
string.sub("abCdef", 3) → "Cdef"
string.sub("abCdef", 3, 4) → "Cd"
截取子串,省略结束索引则到末尾
索引从1开始,支持负数(倒数)
重复 rep
string.rep("ab", 3) → "ababab"
重复拼接字符串
次数需为非负整数
替换 gsub
string.gsub("abCdeC", "C", "**") → "ab**de** 2"
(返回新串+替换次数)
全局替换子串,可指定替换次数(第3参数)
返回两个值:新串、替换次数
转ASCII byte
string.byte("Lua", 1) → 76
(取第1个字符的ASCII码)
提取字符的ASCII码(多字节字符仅取首字节)
索引可选(默认1)
ASCII转字符 char
string.char(76, 117, 97) → "Lua"
将ASCII码转为字符
支持多参数,按顺序拼接
运算符
类型
运算符
示例/特性
算术
+ - * / % ^
5^2 = 25
(幂)、7%3 = 1
(取余)
比较
== ~= > < >= <=
nil == false → false
(仅 nil
/false
为假)
逻辑
and or not
短路特性(如 true and false → false
)
位运算
& | ~ << >>
Lua5.3+ 支持(如 8>>1 = 4
、3&1 = 1
)
三目
不支持
-
条件分支语句 if 条件1 then
-- 分支1
elseif 条件2 then
-- 分支2
else
-- 分支3
end
真值规则 :仅 nil
、false
为假,其余(0、空字符串等)均为真。
循环语句
类型
语法模板
执行逻辑
示例代码(简化)
核心特点/注意事项
while
while 条件 do
-- 循环体
end
先判条件,真则执行循环体
num=0; while num<5 do print(num); num=num+1 end
可能一次不执行;需手动更新条件(否则死循环)
repeat-until
repeat
-- 循环体
until 条件
先执行循环体,再判条件,真则退出
num=0; repeat print(num); num=num+1 until num>5
至少执行一次;退出条件与C# do-while
相反(Lua“条件真→退出”,C#“条件假→退出”)
数值for
for 变量 = 初始值, 终止值, 步长 do
-- 循环体
end
变量按步长增减,超终止值(方向由步长定)则退出
-- 递增(步长1):
for i=2,5 do print(i) end
-- 自定义步长:
for i=1,5,2 do print(i) end
-- 递减:
for i=5,1,-1 do print(i) end
步长可选(默认1);变量局部作用域; 初始值与步长方向矛盾则不执行(如 for i=1,5,-1
)
细节补充 :
数值for的终止规则:
步长为正 → 变量 > 终止值 时退出(如 for i=1,5
→ i=6
退出);
步长为负 → 变量 < 终止值 时退出(如 for i=5,1,-1
→ i=0
退出);
若初始值与步长方向矛盾(如 for i=3,1,1
),循环直接跳过 。
死循环处理:while true
/ repeat ... until false
需用 break
手动终止。
函数核心特性 声明与调用方式
类型
声明语法
调用语法
示例
命名函数
function 函数名(参数) ... end
函数名(参数)
function add(a,b) return a+b end
add(1,2)
匿名函数
local 变量 = function(参数) ... end
变量(参数)
local f = function() print("匿名") end
f()
参数处理
场景
规则说明
示例代码
输出结果
常规参数
按顺序传递,无需声明类型
function F(a) print(a) end
F("str")
str
参数不匹配
多传则丢弃,少传补nil
F()
F(1,2,3)
nil
1
(丢弃2、3)
变长参数
用...
接收,存入arg
表(Lua5.1+)或自定义表
function F(...) local t={...} print(#t) end
F(1,"a",true)
3
(表长度)
返回值机制
类型
语法示例
调用与接收方式
注意事项
单返回值
function F(a) return a*2 end
local res = F(5)
print(res)
10
多返回值
function F(a) return a, "ok", true end
local x, msg, flag = F(10)
print(x, msg, flag)
10 ok true
变量匹配
接收变量不足则忽略多余返回值,多余变量补nil
local x = F(10)
print(x)
10
(忽略”ok”、true
)
特殊特性
特性
说明
示例代码
核心逻辑
函数类型
变量类型为function
local f = function() end
print(type(f))
function
重载限制
同名函数后声明覆盖前声明
function F() print(1) end
function F() print(2) end
F()
2
(调用最后声明的函数)
函数嵌套
内部函数可返回给外部
function F() return function() print("嵌套") end end
local f = F()
f()
嵌套
闭包
内部函数捕获外部变量
function F(x) return function(y) return x+y end end
local f = F(10)
print(f(5))
15
(x=10被持久化)
闭包核心示例 -- 闭包捕获外部变量x,形成持久化状态
function makeAdder(x)
return function(y)
return x + y -- 即使makeAdder执行完毕,x仍被保留
end
end
local add10 = makeAdder(10)
local add20 = makeAdder(20)
print(add10(5)) -- 15
print(add20(5)) -- 25
关键 :闭包延长了外部变量的生命周期,不同闭包实例独立维护各自的捕获变量(如add10
和add20
分别保存x=10和x=20)。
表(Table) 表的本质与基本概念
万能容器 :表是Lua唯一的复合类型,可实现数组、字典、类、结构体等所有复杂数据结构。
动态特性 :无固定结构,运行时可任意增删改查键值对。
数组功能(顺序索引)
功能
语法/示例
注意事项
声明
t = {1, 2, "a", nil, true}
索引默认从1开始,nil
会影响#
计算长度
取值
print(t[1]) → 1
print(t[5]) → true
索引从1开始,0或负数索引需显式声明(如t[0] = "zero"
)
长度
print(#t) → 4
(若t = {1, nil, 3}
则#t=1
,因遇nil
提前终止)
#
操作符不可靠,仅适用于连续非nil元素的数组
遍历
for i=1, #t do print(t[i]) end
推荐用ipairs
替代(见迭代器部分)
字典功能(自定义索引)
功能
语法/示例
注意事项
声明
t = {["name"]="Lua", age=5.4, [100]="key"}
键可以是任意类型(除nil
),值可为任意类型
取值
print(t.name) → "Lua"
.print(t["age"]) → 5.4
数字键不能用.
访问(需[]
),如t[100]
修改/新增
t.name = "LuaNew"
.t["version"] = 5.4
直接赋值,不存在的键自动创建
删除
t.name = nil
.t["version"] = nil
赋值nil
即可删除键值对
遍历
for k, v in pairs(t) do print(k, v) end
必须用pairs
遍历所有键值对
迭代器遍历对比
迭代器
语法
遍历范围
特点
ipairs
for i,v in ipairs(t)
仅顺序索引(1,2,3…)
遇nil
终止,无法遍历自定义索引
pairs
for k,v in pairs(t)
所有索引(含负数、字符串等)
遍历所有键值对,无顺序保证
类与结构体实现(基于表)
功能
语法/示例
核心逻辑
类声明
Student = {age=18, name="Lua"}
function Student:printInfo() print(self.age) end
表存储属性和方法,self
指代实例
实例化
local stu = {}setmetatable(stu, {__index=Student})
通过元表__index
继承类属性和方法
方法调用
stu:printInfo()
(等价于Student.printInfo(stu)
)
:
调用自动传递self
参数
表的公共方法(table.xxx
)
方法名
语法/示例
说明
insert
table.insert(t, 3, "new")
(在索引3插入元素)table.insert(t, "new")
(追加到末尾)
插入元素,自动调整后续索引
remove
table.remove(t, 2)
(删除索引2的元素,返回其值)table.remove(t)
(删除末尾元素)
删除元素,后续元素前移
sort
table.sort(t)
(升序)table.sort(t, function(a,b) return a>b end)
(降序)
排序数组(仅适用于顺序索引表)
concat
table.concat(t, ",")
(用逗号拼接元素)table.concat(t)
(无分隔符)
拼接表元素为字符串
多脚本与模块系统 变量作用域:全局与局部
类型
声明方式
作用域
示例代码
注意事项
全局变量
直接赋值(无local
)
所有脚本可见
a = 10
for i=1,2 do c="test" end
print(c)
(输出”test”)
过多使用消耗内存,污染全局环境
局部变量
local 变量=值
仅当前作用域可见
for i=1,2 do local d="test" end
print(d)
(输出nil
)
函数内建议用local
声明,避免全局污染
模块加载机制:require
阶段
语法/逻辑
示例代码
核心说明
声明模块
脚本末尾return 导出表
-- module.lua
local M={}
M.fun=function() print("hi") end
return M
导出表需包含对外接口,避免直接暴露内部变量
加载模块
local mod = require("模块名")
local utils = require("utils")
utils.fun()
(调用模块方法)
首次加载执行脚本,缓存结果;重复加载直接返回缓存
返回值处理
接收require
的返回值
local localVar = require("script")
print(localVar)
(获取脚本返回的局部变量)
脚本可通过return
暴露局部变量,外部仅能访问返回值
脚本卸载与重新加载
操作
语法/逻辑
示例代码
应用场景
卸载脚本
package.loaded["模块名"] = nil
package.loaded["LuaMultiScriptTest"] = nil
require("LuaMultiScriptTest")
(重新执行脚本)
修改脚本后强制重新加载,测试时常用
检查加载状态
package.loaded["模块名"]
print(package.loaded["utils"])
(返回true
或模块导出值)
判断模块是否已加载,避免重复操作
全局变量存储:_G
表
特殊语法 多变量赋值
规则 :变量与值左对齐匹配,值不足补nil
,值过多忽略。
示例 : a,b,c = 1,2 -- a=1, b=2, c=nil
x,y = y,x -- 交换变量值
多返回值函数
特性 :函数可返回多个值,接收时按顺序匹配。
示例 : function f() return 1,2,3 end
a,b = f() -- a=1, b=2(忽略3)
逻辑运算符(and/or)
短路规则 :
and
:左假返左,左真返右。
or
:左真返左,左假返右。
非布尔值 :除nil/false
外均为真,如1 and 2 → 2
,nil or 3 → 3
。
模拟三目运算符
协同程序(Coroutine) 协程创建与执行对比
类型
创建方法
返回类型
执行方式
特点
线程型
coroutine.create(func)
thread
coroutine.resume(co)
返回首个值为执行状态(true/false
),适合分步控制
函数型
coroutine.wrap(func)
function
co()
直接返回函数结果,无状态返回,使用更简洁
挂起与分步执行
类型
挂起方法
执行结果
示例代码
线程型
coroutine.yield(...)
resume(co)
返回 {true, v1, v2...}
co = create(func); resum(co)
函数型
coroutine.yield(...)
co()
返回 v1, v2...
co = wrap(func); print(co())
协程状态
状态
含义
检查方法
示例场景
dead
执行完毕/未启动
coroutine.status(co)
无yield
的协程执行后
suspended
挂起(可继续执行)
coroutine.status(co)
执行yield
后
running
正在执行
函数内coroutine.status()
协程函数执行中
核心API
方法
作用
示例
coroutine.running()
获取当前协程线程号
函数内print(coroutine.running())
coroutine.yield(...)
挂起协程并返回值
coroutine.yield(1, "ok")
元表基础概念
关系 :元表是表的“父表”,子表执行特定操作时会调用元表中的元方法。
设置/获取 :
setmetatable(子表, 元表)
:设置元表。
getmetatable(子表)
:获取元表。
核心元方法对比
元方法
触发条件
参数
示例代码
说明
__tostring
子表被转为字符串(如print
)
function(子表)
print(子表)
→ 调用元表的__tostring
返回值
自定义表的字符串表示
__call
子表被当作函数调用
function(子表, arg1, arg2...)
子表(1, 2)
→ 执行元表的__call
使表可像函数一样调用
__add
子表参与+
运算
function(表1, 表2)
表1 + 表2
→ 调用__add
返回和
重载加法运算符
__index
子表查找属性不存在时
- 表/函数:function(子表, 键)
- 表:{键=值}
子表.属性
→ 查元表__index
__index=function(t,k) return t[k] or "默认值"
属性查找的fallback机制,可设为函数或表
__newindex
子表赋值属性不存在时
function(子表, 键, 值)
子表.属性=值
→ 调用__newindex
__newindex=function(t,k,v) t[k]=v*2 end
控制属性赋值行为,可设为函数或表
运算重载元方法
运算符
元方法
示例
+
__add
a + b
→ __add(a, b)
-
__sub
a - b
→ __sub(a, b)
*
__mul
a * b
→ __mul(a, b)
/
__div
a / b
→ __div(a, b)
==
__eq
a == b
→ __eq(a, b)
..
(拼接)
__concat
a .. b
→ __concat(a, b)
rawget与rawset(绕过元方法)
函数
作用
示例代码
对比
rawget(表, 键)
直接获取表属性,不触发__index
rawget(t, "key")
→ 直接查t["key"]
与表.键
的区别:后者会触发__index
rawset(表, 键, 值)
直接设置表属性,不触发__newindex
rawset(t, "key", 10)
→ 直接设t["key"]=10
与表.键=值
的区别:后者会触发__newindex
面向对象(模拟) 核心实现基础
概念
实现方式
关键代码示例
类定义
表 + 元表
Object = {}
(万物之父类)
实例化
:new()
方法返回表对象,设置元表
function Object:new() local obj={}; setmetatable(obj,self); self.__index = self end
属性/方法
表字段存储属性,函数存储方法
Object.id=1; function Object:PrintId() print(self.id) end
封装:万物之父类设计
功能
实现逻辑
示例效果
实例化
新建表,设元表为当前类,__index
指向自身
local obj = Object:new()
→ obj
是表,元表为Object
属性访问
实例无属性时查元表__index
obj.id
→ 查Object.id
(默认1),实例赋值后覆盖
方法调用
方法存储在类表,self
传实例
obj:PrintId()
→ 调用Object.PrintId(obj)
继承:子类注册机制
步骤
核心代码
继承链
注册子类
Object:subClass("Person")
→ 在_G
中创建子类表
_G.Person = {}; setmetatable(Person, Object)
属性继承
子类无属性时查父类元表__index
person1.id
→ 查Person.__index
→Object.id
方法继承
子类方法不存在时查父类
person1:PrintId()
→ 调用Object.PrintId
多态:方法重写
场景
父类方法
子类重写
移动方法
GameObject:Move()
→ 坐标+1
Player:Move()
→ 调用父类逻辑后扩展
调用父类
self.base.Move(self)
(显式传实例)
Player:Move()
中通过base
调用父类
多态表现
同一方法名,不同实现
player1:Move()
与 gameObj:Move()
行为不同
关键代码模板
类定义与实例化
Object = {}
setmetatable(Object, Object)
Object.__index = Object
function Object:new()
local obj = {}
setmetatable(obj, self)
self.__index = self
return obj
end
子类继承
function Object:subClass(className)
_G[className] = {}
_G[className].base = self
setmetatable(_G[className], self)
self.__index = self
end
多态方法重写
-- 父类方法
function GameObject:Move()
self.posX+=1; print(self.posX)
end
-- 子类重写
function Player:Move()
self.base.Move(self);
print("Player移动")
end
自带库 时间库(os
)
函数
说明
参数
返回值
示例
time()
获取当前时间戳(秒)
无
数值(时间戳)
print(os.time())
→ 1695818702
time({year, month, day})
生成指定日期的时间戳
表(year
, month
, day
等)
数值(时间戳)
os.time({year=2014, month=8, day=14})
→ 1407988800
date("*t")
获取当前时间的表对象
格式字符串"*t"
表(含year
, month
, day
等字段)
local t=os.date("*t"); print(t.year)
→ 2023
数学库(math
)
函数
说明
参数
返回值
示例
abs(x)
取绝对值
数值x
数值(绝对值)
math.abs(-1)
→ 1
deg(x)
弧度转角度
弧度值x
角度值
math.deg(math.pi)
→ 180
cos(x)
余弦函数(弧度)
弧度值x
余弦值
math.cos(math.pi)
→ -1
floor(x)
向下取整
数值x
整数(≤x的最大整数)
math.floor(2.6)
→ 2
ceil(x)
向上取整
数值x
整数(≥x的最小整数)
math.ceil(5.2)
→ 6
max(a, b)
取最大值
数值a
, b
较大值
math.max(1, 2)
→ 2
min(a, b)
取最小值
数值a
, b
较小值
math.min(4, 5)
→ 4
modf(x)
分离整数和小数部分
数值x
整数部分和小数部分
math.modf(1.2)
→ 1, 0.2
pow(x, y)
幂运算(x^y)
底数x
, 指数y
数值(x的y次方)
math.pow(2, 5)
→ 32
randomseed(seed)
设置随机数种子
种子值seed
无
math.randomseed(os.time())
random(n)
生成[1, n]的随机整数
最大值n
随机整数
math.random(100)
→ 20
(示例值)
sqrt(x)
开平方
数值x
平方根
math.sqrt(4)
→ 2
路径库(package.path
)
功能
说明
操作方式
示例
查看加载路径
获取Lua脚本搜索路径
print(package.path)
输出路径字符串(如;.\?.lua;...
)
修改加载路径
拼接新路径(如添加自定义目录)
package.path = package.path .. ";C:\\"
路径末尾添加;C:\
垃圾回收(GC)
Lua 垃圾回收从 5.1 的增量式三色标记-清除演进到 5.4 的分代式回收,既兼顾低停顿,又优化短命对象的处理。
三色标记机制将对象分为白、灰、黑三种状态:
白色 :未检查、潜在垃圾;
灰色 :已可达、待扫描其子引用;
黑色 :自身及子对象均已扫描、安全保留。
分代回收将“新生代”对象高频回收、“老生代”对象低频处理,通过晋升策略减少对长寿对象的重复扫描。
弱引用表(__mode="k"|"v"|"kv"
)让缓存或循环引用的一端不阻止 GC,自动释放不再需要的对象,避免内存泄漏。
终结器(__gc
元方法)为 userdata
提供析构式清理入口,在对象回收前执行自定义逻辑,确保外部资源(文件、网络、C 内存等)得到正确释放。
collectgarbage()
系列接口允许:
统计内存("count"
)、触发完整回收("collect"
);
调节回收阈值("setpause"
)与步长("setstepmul"
);
手动步进("step"
)、暂停/重启 GC("stop"
/"restart"
)。
在性能敏感场景(如游戏关键帧或热更新逻辑)建议结合手动触发以控制停顿。
深拷贝 浅拷贝与深拷贝对比
类型
浅拷贝(=)
深拷贝(自定义实现)
基本类型 (string/number/boolean)
复制值,新变量与原变量独立
同浅拷贝(无需额外处理)
表(table)
复制引用,修改新表会影响原表
递归复制所有层级表,新表与原表完全独立
示例
tbl2=tbl1; tbl2.x=4 → tbl1.x=4
tbl2=clone(tbl1); tbl2.x=4 → tbl1.x=1
深拷贝实现核心逻辑
步骤
代码要点
说明
1. 缓存表防循环引用
local lookup_table = {}
记录已拷贝的表,避免循环引用导致无限递归
2. 处理基本类型
if type(object)~="table" then return object
非表类型直接返回(值复制)
3. 处理已拷贝表
elseif lookup_table[object] then return lookup_table[object]
若表已拷贝,直接返回缓存的新表
4. 递归拷贝表
new_table[_copy(key)] = _copy(value)
对表的每个键值对递归调用拷贝函数,键和值都可能是表
5. 保留元表
setmetatable(new_table, getmetatable(object))
新表继承原表的元表,保持行为一致性
核心代码示例 function clone(obj)
local lookup = {}
local function _copy(o)
if type(o) ~= "table" then return o end
if lookup[o] then return lookup[o] end
local new = {}
lookup[o] = new
for k, v in pairs(o) do
new[_copy(k)] = _copy(v)
end
return setmetatable(new, getmetatable(o))
end
return _copy(obj)
end
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com