当我们在全局环境中定义变量时经常会有命名冲突,尤其是在使用一些库的时候,变量声明可能会发生覆盖,这时候就需要一个非全局的环境来解决这问题。setfenv函数可以满足我们的需求。
setfenv(f, table):设置一个函数的环境
(1)当第一个参数为一个函数时,表示设置该函数的环境
(2)当第一个参数为一个数字时,为1代表当前函数,2代表调用自己的函数,3代表调用自己的函数的函数,以此类推
所谓函数的环境,其实一个环境就是一个表,该函数被限定为只能访问该表中的域,或在函数体内自己定义的变量。下面这个例子,设定当前函数的环境为一个空表,那么在设定执行以后,来自全局的print函数将不可见,所以调用会失败。
-- 一个环境就是一个表,该表记录了新环境能够访问的全部域
newfenv = {}
setfenv(1, newfenv)
print(1) -- attempt to call global `print' (a nil value)
我们可以这样继承已有的域:
a = 10
newfenv = {_G = _G}
setfenv(1, newfenv)
_G.print(1) -- 1
_G.print(_G.a) -- 10
_G.print(a) -- nil 注意此处是nil,新环境没有a域,但可以通过_G.a访问_G的a域
可以看到,新环境中可以访问_G,但有一点就是_G中的所有函数必须手动调用,这样其实很不方便。我们可以使用metatable来对上述代码进行改进:
-- 任何赋值操作都对新表进行,不用担心误操作修改了全局变量表。另外,你仍然可以通过_G修改全局变量:
newfenv = {}
setmetatable(newfenv, {__index = _G})
setfenv(1, newfenv)
print(1) -- 1 新环境直接继承了全局环境的所有域,好处:可以不需要通过_G来手动调用
这样,当访问到函数中不存在的变量时,会自动在_G中查找。对于当前函数和_G都存在的变量,可以通过是否用_G显示调用来区分,比如如果有两个a,那么_G.a表示继承来的,a就是当前函数环境的。
另外,可以通过getfenv(f)函数查看函数所处的环境,默认会返回全局环境_G。
- f = 4
- function a()
- f = 3
- print(getfenv(0).f, getfenv(1).f, getfenv(2).f, getfenv(3).f)
- end
- A = {}
- setmetatable(A, {__index = _G})
- setfenv(a, A)
- function b()
- f = 2
- A.a()
- end
- B = {}
- setmetatable(B, {__index = _G})
- setfenv(b, B)
- function c()
- f = 1
- B.b()
- end
- C = {}
- setmetatable(C, {__index = _G})
- setfenv(c, C)
- c()
以上是代码块,函数也同理。
function test ()
local xx = 11
local env = {name = "do"}
setmetatable(env, {__index = _G})
setfenv(1,env)
print(getfenv(1).name,getfenv(2).name,getfenv(1).xx,getfenv(2).xx)
end
function test1()
local xx = 22
local env = {name = "brag",xx = 33}
setmetatable(env, {__index = _G})
setfenv(1,env)
test()
end
test1()
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
---------------------
作者:whereismatrix
来源:CSDN
原文:https://blog.csdn.net/whereismatrix/article/details/79704421
只有setfenv了环境。。getfenv才能生效。