>> >> >> Reference << << << <<<<<<Ref>>>>>>
Scopes, Variables, and Environments
Modified: 2026-02-23 | Author:ljf12825

变量类型

Lua中有三种变量:全局变量、局部变量和表字段

全局变量

在默认情况下,在代码中直接赋值的变量默认都是全局变量

-- 定义一个变量
g = 100

function foo()
    -- 在函数内部访问和修改全局变量
    g = g + 10
    print("inside foo, g =", g) -- 输出:inside foo, g = 110
end

foo()
print("outside, g =", g) -- 输出:outside, g = 110

-- 再定义一个全局变量
a = 1
b = 2

function bar()
    -- 这里没有使用local声明,所以c也是全局变量
    c = a + b
    print("c =", c) -- 输出:c = 3
end

bar()
print(c) -- 输出3

全局变量存储在一个叫做_G的全局表中。访问全局变量x本质上等于访问_G["x"]

x = 10 -- 全局变量,存在_G.x中
print(_G["x"]) -- 输出 10

全局变量在整个程序运行期间都存在,未赋值前值为nil

局部变量

local声明,作用域限定在声明所在的块(block)内

local a = 1

do
    local a = 2 -- 新的局部变量,遮蔽外层的 a
    print(a) -- 2
end

print(a) -- 1,内层的 a 已超出作用域

局部变量的好处:访问速度更快(通过寄存器/栈访问,而非哈希表查找),并且避免污染全局命名空间

作用域(Scope)

Lua是词法作用域(Lexical Scoping),作用域在编译期确定,而非运行期

Chunk

块(Block)

作用域以块为单位,以下结构都会产生新的块

for i - 1, 3 do
    local x = i * 2 -- x 的作用域仅在 for 循环体内
end
-- print(x) -- error! x 已不可见

变量遮蔽(Shadowing)

内层块可以声明同名局部变量,遮蔽外层变量

local x = "outer"
do
    local x = "inner"
    print(x) -- inner
end
print(x) -- outer

闭包与上值(Upvalue)

这是Lua作用域最精妙的一点。当一个函数引用了外层函数的局部变量时,该变量就成为上值(upvalue),函数与它捕获的上值共同构成闭包(closure)

function makeCounter()
    local count = 0
    return function()
        count = count + 1
        return count
    end
end

local c1 = makeCounter()
local c2 = makeCounter()

print(c1()) -- 1
print(c1()) -- 2
print(c2()) -- 1 c2 有自己独立的 count

同一个外层函数产生的多个闭包,共享同一个upvalue

function sharedUpvalue()
    local x = 0
    local function inc() x = x + 1 end
    local function get() return x end
    return inc, get
end

local inc, get = sharedUpvalue()
inc(); inc()
print(get()) -- 2, inc和get共享同一个 x

Environment

环境这个概念在Lua5.1和5.2中有较大的变换。但核心思想是:环境是用来存储全局变量的地方

全局环境

Lua 5.1中,所有全局变量都存储在_G这个表里,这个表就是全局环境
访问全局变量print等价于_G.print,Lua实际上是在_G表中查找键为"print"的值
可以直接操作_G

-- 定义一个全局变量
_G["dynamicVar"] = 42
print(dynamicVar)

-- 以下两行是等价的
print("Hello")
_G["print"]("Hello")

也可以遍历所有全局变量

for k, v in pairs(_G) do
    print(k, v)
end

函数环境

可以改变一个函数(或一个chunk)的环境,让它在另一个环境中查找“全局”变量。这对于实现沙箱(sandbox)非常有用

Lua5.1的setfenv/getfenv(历史)

-- Lua 5.1写法
local sandbox = {}
setfenv(1, sandbox) -- 将当前函数的环境设为 sandbox
x = 10 -- x 进入 sandbox, 而非 _G

Lua5.2+的环境变化

Lua5.2移除了旧的setfenv/getfenv,改用_ENV机制
它是一个局部变量,存储了当前chunk的“全局”环境
每个函数都有一个隐式的上值_ENV,所有全局变量访问本质上都是对_ENV的字段访问
可以通过覆盖_ENV来改变一个代码块的环境

-- 等价
x = 10
_EVN.x = 10

_G只是初始_ENV的默认值,可以替换_ENV来改变函数的“全局”环境

local myEnv = { print = print } -- 给新环境一个 print 函数

-- 通过local 指定环境
local f = load("x = 100; print(x)", "chunk", "t", myEnv)
f()
print(myEnv.x) -- 100
print(x) -- nil, x没有进入真正的 _G