第十二章:最佳实践与优化

写出“能运行”的代码只是第一步,写出“高效、易读、易维护”的代码才是我们的目标。在本章,我们将总结 Lua 编程中的一些最佳实践和性能优化技巧。

12.1 代码风格与规范

良好的代码风格有助于团队协作和后期维护。

1. 局部变量优先

始终使用 local 声明变量,除非你确实需要全局变量。

  • 性能:局部变量的访问速度比全局变量快得多。

  • 安全:避免污染全局命名空间,减少变量冲突。

  • GC:局部变量的作用域结束时,引用解除,有利于垃圾回收。

-- Bad
count = 0 
function inc() count = count + 1 end

-- Good
local count = 0
local function inc() count = count + 1 end

2. 命名约定

  • 变量/函数:小写字母开头,驼峰命名 (e.g., userName, calculateTotal) 或下划线分隔 (e.g., user_name, calculate_total)。保持一致即可。

  • 常量:全大写,下划线分隔 (e.g., MAX_RETRY_COUNT)。

  • 类/模块:大写字母开头 (e.g., HttpServer, StringUtil)。

  • 私有成员:下划线开头 (e.g., _internalValue),这只是一种约定。

3. 注释

  • 函数上方应说明功能、参数和返回值。

  • 复杂逻辑代码块需要注释解释“为什么这么做”。

12.2 性能优化技巧

Lua 本身已经很快,但糟糕的代码会拖累性能。

1. 缓存全局变量

如果在循环或高频调用的函数中访问全局变量(包括标准库函数),应将其缓存为局部变量。

-- Bad
for i = 1, 1000000 do
    x = math.sin(i)
end

-- Good
local sin = math.sin
for i = 1, 1000000 do
    x = sin(i)
end

原因:访问局部变量只需读取栈或寄存器,而访问全局变量需要查找全局表(哈希表查找)。

2. 字符串拼接

在循环中进行大量字符串拼接时,使用 table.concat 而不是 ..

-- Bad (产生大量中间字符串对象,增加 GC 压力)
local s = ""
for i = 1, 1000 do
    s = s .. "a"
end

-- Good
local t = {}
for i = 1, 1000 do
    t[#t + 1] = "a"
end
local s = table.concat(t)

3. 表的预分配

如果你知道表的大致大小,预先填充 nil 或者使用 C API 创建预分配大小的表(在纯 Lua 中较难做到,但可以通过构造器暗示)。
更重要的是,避免在循环中创建表

-- Bad
for i = 1, 10000 do
    local point = {x = i, y = i} -- 每次循环都创建新表
    process(point)
end

-- Good (如果 process 不保存 point 的引用)
local point = {}
for i = 1, 10000 do
    point.x = i
    point.y = i
    process(point)
end

4. 减少闭包创建

闭包(Closure)也是对象。在循环中创建函数会产生大量闭包实例。

-- Bad
for i = 1, 100 do
    doSomething(function() return i end) -- 每次创建新闭包
end

-- Good (将函数移出循环,如果逻辑允许)
local function callback() ... end
for i = 1, 100 do
    doSomething(callback)
end

12.3 安全编码

1. 防止注入

在拼接 SQL 语句或系统命令(os.execute)时,务必对用户输入进行过滤或转义,防止注入攻击。

2. 保护全局环境

可以使用 setmetatable(_G, {__newindex = ...}) 来监控或禁止全局变量的创建,防止意外拼写错误导致创建了全局变量。

local mt = {
    __newindex = function(_, n)
        error("attempt to write to undeclared variable " .. n, 2)
    end,
    __index = function(_, n)
        error("attempt to read undeclared variable " .. n, 2)
    end
}
setmetatable(_G, mt)

12.4 结语

Lua 是一门“小而美”的语言。它的简洁性赋予了开发者极大的自由,但自由也意味着责任。

通过本系列教程的学习,你已经掌握了 Lua 的核心语法、常用库、与 C 的交互以及实战技巧。但这只是开始,Lua 在游戏开发(OpenResty, Love2D)、嵌入式(NodeMCU)、工具脚本等领域还有广阔的天地等待你去探索。

Keep Coding, Keep Learning!


教程全系列完结。