lua5.1中的全局环境变量
Lua将环境table保存在一个全局变量_G中,可以对其访问和设置。一般我们把需要被访问的对象和函数等放到这里,
然后我们可以在需要时通过它来访问和使用。
可以通过value = _G["varname"]
或者value = _G.varname
来获得动态名字的全局变量。
> a=1
> b= "b"
> print(_G[a])
nil
> print(_G["a"])
1
> print(_G["b"])
b
> print(_G.a)
1
> print(_G.b)
b
“环境”是全局的,任何对它的修改都会影响程序的所有部分。对简单的使用不会有什么问题,但较复杂的应用,这个会是一大潜在的问题
Lua5.1中改变环境 - setfenv
Lua5.1允许每个函数拥有一个子集的环境来查找全局变量,可以通过setfenv来改变一个函数的环境,
第一个参数若是1则表示当前函数,2则表示调用当前函数的函数(依次类推),第二个参数是一个新的环境table。
比如下面,修改了函数的环境, print不存在了,再访问就会出错。
va = 1
setfenv(1, {})
print(va) -- 会报错,print是一个nil。这是因为一旦改变环境,所有的全局访问都会使用新的table
为了避免上述问题,可以使用
setfenv(1, {_G = _G})
将原来的环境保存起来,然后用_G.print来引用。
另一种组装新环境的方法是使用继承,下面的代码新环境从源环境中继承了print和a,任何赋值都发生在新的table中。
a = 1
local newgt = {}
setmetatable(newgt, {__index = _G})
setfenv(1, newgt)
print(a)
或者
a = 1
local newgt = {}
setmetatable(newgt, {__index = _G})
示例:
a = "1" -- create a global variable
print(a, getmetatable(a))
local t ={}
setmetatable(t, {__index = _G})
setfenv(1, t) -- 设置当前的func-env为t
print(a)
----
_G.print("_G:", a) -- _G: 1
t.print("t: ", a) -- t: 1
setfenv(a, {}) -- change current environment to a new empty table
print(a) -- attempt to call global 'print' (a nil value)
Lua5.1之后的环境
在5.2之后, 引入了_ENV叫做环境,与_G全局变量表产生了一些混淆。
在5.2中, 操作a = 1
相当于_ENV['a'] = 1
这是一个最基础的认知改变,其次要格外注意_ENV不是全局变量,而是一个upvalue(非局部变量)。
其次,_ENV[‘_G’]指向了_ENV自身,这一目的是为了兼容5.1之前的版本,因为之前你也许会用到:
_G['a'] = 2
, 在5.2中, 这相当于_ENV[‘_G’][‘a’],为了避免5.1之前的老代码在5.2中运行错误,所以5.2设置了_ENV[‘_G’]=_ENV来兼容这个问题。然而你不要忘记_ENV[‘_G’]=_ENV,所以一切都顺理成章了。
> a = 1
> _ENV[a]
nil
> _ENV["a"]
1
> _ENV["_G"]
table: 0x7fab5c5002e0
> print(_ENV)
table: 0x7fab5c5002e0
>
> _G["a"]
1
> _ENV._G.a
1
> _ENV["_G"].a
1
> _ENV["_G"]["a"]
1
>
在5.1中,我们可以为一段代码块(或者函数)设置环境,使用函数setfenv,这样会导致那一段代码/数访问全局变量的时候使用了setfuncs指定的table,而不是全局的_G。
在5.2中,setfenv遭到了废弃,因为引入了_ENV。 通过在函数定义前覆盖_ENV变量即可为函数定义设置一个全新的环境,比如:
a = 3
function echo()
local _ENV={print=print, a = 2}
function _echo()
_ENV.print(a)
end
return _echo;
end
print(a) -- 3
----
local newEcho = echo()
print(newEcho) -- function: 0x7fd1b94065c0
newEcho() -- 2