Chapter 14: The Environment
Lua 把所有全局变量都存在一个普通的表environment 里面。(更准确的说,是多个环境)这样做的好外是简化了lua 的内部实现。environment 本身又存在_G 里面。
打印当前已定义的所有全局变量
for n in pairs(_G) do print(n) end
14.1 Global Variables with Dynamic Names
function getfield (f)
local v = _G -- start with the table of globals
for w in string.gmatch(f, "[%w_]+") do
v = v[w]
end
return v
end
function setfield (f, v)
local t = _G -- start with the table of globals
for w, d in string.gmatch(f, "([%w_]+)(.?)") do
if d == "." then -- not last field?
t[w] = t[w] or {} -- create table if absent
t = t[w] -- get the table
else -- last field
t[w] = v -- do the assignment
end
end
end
setfield("t.x.y", 10) -- creates a global table t, another table t.x, and assigns 10 to t.x.y
print(t.x.y) --> 10
print(getfield("t.x.y")) --> 10
14.2 Global-Variable Declarations
全局变量无需声明。虽然这对小程序提供了便利,但大程序中易引起难以找出的错误。可以改变这一行为,如果我们愿意。因为Lua 将全局变量存在一个普通的表里面,当访问全局变量的时侯,我们可以使用元表改变它的行为。
检测以不存在的key访问全局表
setmetatable(_G, {
__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,
})
print(a) -- stdin:1: attempt to read undeclared variable a
但是,我们如何声明新变量?一种方法是使用rawset ,它会忽略元方法:
不允许未经声明就使用全局变量
function declare (name, initval)
rawset(_G, name, initval or false)
end
setmetatable(_G, {
__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,
})
a=1 -- stdin:1: attempt to read undeclared variable a
declare "a"
a=1 -- OK
print(a)
只允许在main chunk 中定义全局变量
function declare (name, initval)
rawset(_G, name, initval or false)
end
setmetatable(_G, {
__newindex = function (t, n, v)
local w = debug.getinfo(2, "S").what
if w ~= "main" and w ~= "C" then
error("attempt to write to undeclared variable " .. n, 2)
end
rawset(t, n, v)
end,
__index = function (_, n)
error("attempt to read undeclared variable " .. n, 2)
end,
})
a=1 -- OK
print(a)
检测一个变量是否存在,我们不能简单的与nil 比较,而是使用rawget:
if rawget(_G, var) == nil then
-- 'var' is undeclared
...
end
14.3 Non-Global Environments
Lua 允许每个函数有自已的环境。使用setfenv (set function environment) 改变函数环境。
a = 1 -- create a global variable
-- change current environment to a new empty table
setfenv(1, {})
print(a)
-------------------------------------------
a = 1 -- create a global variable
setfenv(1, {g = _G}) -- change current environment
g.print(a) --> nil
g.print(g.a) --> 1
--------------------
a = 1
local newgt = {} -- create new environment
setmetatable(newgt, {__index = _G})
setfenv(1, newgt) -- set it
print(a) --> 1
a = 10
print(a) --> 10
print(_G.a) --> 1
_G.a = 20
print(_G.a) --> 20
-------------------------
function factory ()
return function ()
return a -- "global" a
end
end
a = 3
f1 = factory()
f2 = factory()
print(f1()) --> 3
print(f2()) --> 3
setfenv(f1, {a = 10})
print(f1()) --> 10
print(f2()) --> 3