变量与作用域 #

一、变量基础 #

1.1 变量声明 #

Lua 中变量不需要类型声明,直接赋值即可:

lua
-- 直接赋值
name = "Lua"        -- 全局变量
local version = 5.4 -- 局部变量

-- 多变量赋值
local a, b, c = 1, 2, 3

-- 先声明后赋值
local x, y
x = 10
y = 20

1.2 变量命名 #

lua
-- 合法的变量名
local name = "张三"
local user_name = "合法"
local _private = "合法"
local userName = "合法"

-- 非法的变量名
-- local 2name = "非法"      -- 不能以数字开头
-- local user-name = "非法"   -- 不能包含连字符
-- local if = "非法"          -- 不能使用关键字

1.3 变量默认值 #

lua
-- 未赋值的变量默认为 nil
local x
print(x)  -- nil

-- 显式赋值为 nil 可以删除变量
local y = 10
y = nil
print(y)  -- nil

二、局部变量 #

2.1 使用 local 声明 #

lua
-- 使用 local 声明局部变量
local count = 0
local name = "Lua"
local pi = 3.14159

-- 局部函数
local function greet()
    print("Hello!")
end

2.2 作用域范围 #

lua
-- 代码块作用域
do
    local x = 10
    print(x)  -- 10
end
-- print(x)  -- 错误:x 不可见

-- 函数作用域
local function test()
    local y = 20
    print(y)  -- 20
end
test()
-- print(y)  -- 错误:y 不可见

-- 循环作用域
for i = 1, 3 do
    local loop_var = i * 2
    print(loop_var)
end
-- print(i)        -- nil(for 的变量在循环外不可见)
-- print(loop_var) -- 错误

2.3 变量遮蔽 #

lua
local x = 1

do
    local x = 2  -- 遮蔽外层的 x
    print(x)     -- 2
    
    do
        local x = 3  -- 遮蔽中间层的 x
        print(x)     -- 3
    end
    
    print(x)     -- 2
end

print(x)         -- 1

三、全局变量 #

3.1 默认全局 #

lua
-- 不使用 local 声明的变量是全局变量
name = "Lua"

function show()
    print(name)  -- 可以访问全局变量
end

show()  -- Lua

3.2 全局变量表 _G #

lua
-- 所有全局变量都存储在 _G 表中
name = "Lua"
print(_G.name)  -- Lua

-- 遍历所有全局变量
for k, v in pairs(_G) do
    print(k, v)
end

-- 动态创建全局变量
local key = "dynamic_var"
_G[key] = "动态创建的值"
print(dynamic_var)  -- 动态创建的值

3.3 检测全局变量 #

lua
-- 检查全局变量是否存在
if _G["myVar"] ~= nil then
    print("myVar 存在")
end

-- 使用 rawget 避免 __index 元方法
if rawget(_G, "myVar") ~= nil then
    print("myVar 存在")
end

3.4 全局变量的危险 #

lua
-- 意外创建全局变量
function calculate(a, b)
    result = a + b  -- 忘记 local!
    return result
end

-- 检测全局变量泄漏
local function check_globals()
    local known_globals = {}
    for k in pairs(_G) do
        known_globals[k] = true
    end
    
    return function()
        for k in pairs(_G) do
            if not known_globals[k] then
                print("新全局变量:" .. k)
            end
        end
    end
end

四、作用域规则 #

4.1 词法作用域 #

Lua 使用词法作用域(静态作用域):

lua
local x = 10

local function foo()
    print(x)  -- 访问外层的 x
end

local function bar()
    local x = 20
    foo()     -- foo 仍然访问外层的 x = 10
end

bar()  -- 10

4.2 闭包与作用域 #

lua
-- 闭包捕获外层变量
local function counter()
    local count = 0
    return function()
        count = count + 1
        return count
    end
end

local c = counter()
print(c())  -- 1
print(c())  -- 2
print(c())  -- 3

-- 另一个计数器,独立的 count
local c2 = counter()
print(c2())  -- 1

4.3 作用域链 #

lua
local a = 1

local function outer()
    local b = 2
    
    local function inner()
        local c = 3
        print(a, b, c)  -- 可以访问所有外层变量
    end
    
    inner()
    -- print(c)  -- 错误:c 不可见
end

outer()
-- print(b)  -- 错误:b 不可见

五、最佳实践 #

5.1 优先使用局部变量 #

lua
-- 推荐:使用局部变量
local function process()
    local result = 0
    for i = 1, 100 do
        result = result + i
    end
    return result
end

-- 不推荐:使用全局变量
function process()
    result = 0  -- 全局变量
    for i = 1, 100 do
        result = result + i
    end
    return result
end

5.2 缓存全局访问 #

lua
-- 缓存常用全局函数
local print = print
local table = table
local string = string

-- 性能更好的循环
local concat = table.concat
local result = {}
for i = 1, 10000 do
    result[i] = tostring(i)
end
print(concat(result, ","))

5.3 限制变量作用域 #

lua
-- 使用 do...end 限制作用域
local result

do
    local temp1 = calculate_step1()
    local temp2 = calculate_step2(temp1)
    result = calculate_step3(temp2)
    -- temp1, temp2 在这里自动释放
end

-- 只有 result 在后续可用
print(result)

5.4 避免全局污染 #

lua
-- 使用模块模式
local M = {}

M.version = "1.0"

function M.hello()
    print("Hello!")
end

return M

-- 或使用严格模式检测
local function strict_mode()
    local mt = {
        __newindex = function(t, k, v)
            error("禁止创建全局变量:" .. k, 2)
        end,
        __index = function(t, k)
            error("访问未定义的全局变量:" .. k, 2)
        end
    }
    setmetatable(_G, mt)
end

六、特殊变量 #

6.1 _G 全局表 #

lua
-- _G 是全局变量表
print(_G._G == _G)  -- true

-- 遍历标准库
for name, value in pairs(_G) do
    if type(value) == "table" then
        print("库:" .. name)
    end
end

6.2 _ENV 环境表(Lua 5.2+) #

lua
-- _ENV 是当前环境
local print = print  -- 保存原始 print

-- 创建新环境
local env = {print = print, x = 10}
local code = [[print("x = " .. x)]]

-- 在新环境中执行
local func, err = load(code, "test", "t", env)
if func then
    func()
end
-- 输出:x = 10

6.3 _VERSION 版本 #

lua
print(_VERSION)  -- Lua 5.4

-- 版本检测
if _VERSION == "Lua 5.1" then
    -- Lua 5.1 特定代码
elseif _VERSION == "Lua 5.4" then
    -- Lua 5.4 特定代码
end

6.4 arg 命令行参数 #

lua
-- arg[0] 是脚本名
-- arg[1] 到 arg[n] 是参数
print("脚本:" .. arg[0])
for i = 1, #arg do
    print("参数 " .. i .. ":" .. arg[i])
end

七、变量生命周期 #

7.1 局部变量生命周期 #

lua
-- 局部变量在作用域结束时销毁
local function create()
    local t = {1, 2, 3}
    return function()
        return t  -- 闭包延长了 t 的生命周期
    end
end

local get_t = create()
print(#get_t())  -- 3

7.2 垃圾回收 #

lua
-- 手动触发垃圾回收
local t = {1, 2, 3}
t = nil  -- 标记为可回收
collectgarbage()  -- 立即回收

-- 查看内存使用
print(collectgarbage("count"))  -- KB 为单位

八、实用模式 #

8.1 单例模式 #

lua
-- 使用局部变量实现单例
local singleton = nil

local function get_instance()
    if not singleton then
        singleton = {
            value = 0,
            increment = function(self)
                self.value = self.value + 1
            end
        }
    end
    return singleton
end

8.2 私有变量 #

lua
-- 使用闭包实现私有变量
local function create_counter()
    local count = 0  -- 私有变量
    
    return {
        increment = function()
            count = count + 1
            return count
        end,
        get = function()
            return count
        end,
        reset = function()
            count = 0
        end
    }
end

local counter = create_counter()
print(counter.increment())  -- 1
print(counter.increment())  -- 2
print(counter.get())        -- 2
counter.reset()
print(counter.get())        -- 0

8.3 配置模式 #

lua
-- 使用局部表存储配置
local config = {
    host = "localhost",
    port = 8080,
    timeout = 30
}

-- 冻结配置(防止修改)
local function freeze(t)
    local proxy = {}
    local mt = {
        __index = t,
        __newindex = function()
            error("配置不可修改", 2)
        end
    }
    setmetatable(proxy, mt)
    return proxy
end

config = freeze(config)

九、常见错误 #

9.1 忘记 local #

lua
-- 错误:忘记 local
function process()
    result = 0  -- 全局变量!
    for i = 1, 10 do
        result = result + i
    end
    return result
end

-- 正确:使用 local
function process()
    local result = 0
    for i = 1, 10 do
        result = result + i
    end
    return result
end

9.2 变量未定义 #

lua
-- 错误:使用未定义的变量
print(unknownVar)  -- nil(不会报错,但可能是 bug)

-- 正确:检查变量
if unknownVar ~= nil then
    print(unknownVar)
end

9.3 作用域混淆 #

lua
-- 错误:期望修改外层变量
local count = 0
local function increment()
    local count = count + 1  -- 创建新的局部变量
end
increment()
print(count)  -- 0

-- 正确:直接修改
local count = 0
local function increment()
    count = count + 1  -- 修改外层变量
end
increment()
print(count)  -- 1

十、总结 #

本章介绍了 Lua 的变量和作用域:

  1. 变量声明:使用 local 声明局部变量
  2. 作用域规则:词法作用域,变量在代码块内有效
  3. 全局变量:存储在 _G 表中,应尽量避免使用
  4. 闭包:函数可以捕获外层变量
  5. 最佳实践:优先使用局部变量,缓存全局访问

理解变量和作用域是编写高质量 Lua 代码的基础。下一章,我们将学习 Lua 的基本数据类型。

最后更新:2026-03-27