【Lua进阶系列】环境ENV
大家好,我是Lampard猿奋~~
欢迎来到Lua进阶系列的博客,今天和大家分享一下lua中关于环境的一些知识
(一)全局环境_G
lua使用一个表来保存全局变量,一方面简化了Lua语言内部的实现,另一方面可以像操作一个普通表一样操作这个表。lua把全局环境本身保存到全局变量_G中(因此_G._G 和 _G是等价的),我们可以通过_G来访问/设置全局变量。
Lua中的全局变量不需要声明就可以使用,虽然这种行为对于小程序来说可能会比较方便。但是对于大型项目来说可能会引起一些BUG,因为_G就是一个表结构,因此我们可以利用元表的机制来避免这种情况。
对不存在的key赋值:
访问不存在的key:
那么如果我们需要声明一个新的全局变量的时候,使用rawset函数就可以了。rawset可以绕过元方法直接对表复制。
Lua5.3参考文档对rawset的解释:
Lua不允许值为nil的全局变量,因为值为nil的全局变量都会被自动地认为自己是未声明的。但是,要允许值为nil的全局变量也不难,只需要引入一个辅助表来保存已声明的名称即可。
(二)非全局环境_ENV
lua 5.2 正式发布了,对于 lua 语言本身的修改,重中之重就是对 environment 这个概念的修改,本质上,lua 取消了原有意义上的 environment,而是通过使用非全局变量_ENV(上值upvalue)来保存这个全局环境 。全局变量实际上只是一个语法糖,编译时再前面加上了 _ENV.
的前缀。这样,从 load 开始,第一个 chunk 就被加上了 _ENV
这个 upvalue ,然后依次传递下去。
简单来说其实就是经历了这三个步骤
- 编译器在编译所有代码段之前,在外层创建局部变量_ENV
- 编译器将所有的自由名称var变换为_ENV.var
- 函数load使用全局环境(保存在_G)初始化代码段的第一个上值,即Lua语言内部维护的一个普通的表
当我们声明一个"全局"变量时,其实是把这个变量声明到用全局环境初始化的上值_ENV中而已。当我们把_ENV置空之后就会丢失掉全局函数的环境。
这么做的好处是什么呢
在我看来,这就有点像C++中的命名空间,一方面能够保护_G这个全局环境不被污染,另一方面则是避免了同名函数发生覆盖的情况。
如果想调用某模块的全局函数需要先引入该模块
如果调用不同模块之间的同名函数,那么会调用最后require的模块函数
最后,其实_ENV也不一定非要使用全局环境初始化,我们可以通过loadfile中的可选参数,给这个模块创立一个新的环境。这样做的好处是哪怕此时有恶意代码进入该模块,也无法访问到其他全局数据。