Lua非全局的环境

非全局的环境

全局环境的一个问题是,任何修改都会影响你的程序的所有部分。例如,当你安装一个 metatable 去控制全局访问时,你的整个程序都必须遵循同一个指导方针。如果你想使用标准库,标准库中可能使用到没有声明的全局变量,你将碰到坏运。

Lua 5.0 允许每个函数可以有自己的环境来改善这个问题,听起来这很奇怪;毕竟,全局变量表的目的就是为了全局性使用。然而在 Section 15.4 我们将看到这个机制带来很多有趣的结构,全局的值依然是随处可以获取的。
可以使用 setfenv 函数来改变一个函数的环境。Setfenv 接受函数和新的环境作为参数。除了使用函数本身,还可以指定一个数字表示栈顶的活动函数。数字 1 代表当前函数,数字 2 代表调用当前函数的函数(这对写一个辅助函数来改变他们调用者的环境是很方便的)依此类推。下面这段代码是企图应用 setfenv 失败的例子:

a = 1 -- create a global variable 
-- change current environment to a new empty table 
setfenv(1, {}) 
print(a) 
导致:
stdin:5: attempt to call global `print' (a nil value) 

(你必须在单独的 chunk 内运行这段代码,如果你在交互模式逐行运行他,每一行都是一个不同的函数,调用 setfenv 只会影响他自己的那一行。)一旦你改变了你的环境,所有全局访问都使用这个新的表,如果她为空,你就丢失所有你的全局变量,甚至_G,所以,你应该首先使用一些有用的值封装(populate)她,比如老的环境:

a = 1 -- create a global variable 
-- change current environment 
setfenv(1, {_G = _G}) 
_G.print(a) --> nil 
_G.print(_G.a) --> 1 
现在,当你访问"global" _G,他的值为旧的环境,其中你可以使用 print 函数。你也可以使用继承封装(populate)你的新的环境:
a = 1 
local newgt = {} -- create new environment 
setmetatable(newgt, {__index = _G}) 
setfenv(1, newgt) -- set it 
print(a) --> 1 

在这段代码新的环境从旧的环境中继承了 print 和 a;然而,任何赋值操作都对新表进行,不用担心误操作修改了全局变量表。另外,你仍然可以通过_G 修改全局变量:

-- continuing previous code 
a = 10 
print(a) --> 10 
print(_G.a) --> 1 
_G.a = 20 
print(_G.a) --> 20 

当你创建一个新的函数时,他从创建他的函数继承了环境变量。所以,如果一个chunk 改变了他自己的环境,这个 chunk 所有在改变之后定义的函数都共享相同的环境,都会受到影响。这对创建命名空间是非常有用的机制

发布了290 篇原创文章 · 获赞 153 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_39885372/article/details/104390704