leg*_*s2k 7 environment lua global-variables lua-table
摘录自Lua 5.3手册:
_G
保存全局环境的全局变量(不是函数)(参见§2.2).Lua本身不使用这个变量; 改变其价值不会影响任何环境,反之亦然.
相关部分来自§2.2
[...]每个块都在一个名为的外部局部变量的范围内编译
_ENV,因此_ENV它本身永远不是块中的自由名称.[...]
用作值的任何表
_ENV称为环境.Lua保持着一个称为全球环境的杰出环境.此值保存在C注册表中的特殊索引处.在Lua中,全局变量
_G使用相同的值进行初始化.(_G从不在内部使用.)当Lua加载块时,其
_ENVupvalue 的默认值是全局环境.因此,默认情况下,Lua代码中的自由名称是指全局环境中的条目
我的理解是,每块装,因为_ENV是第一的upvalue,它指出,全球环境表,通过指出_G的load.
> =_G, _ENV
table: 006d1bd8 table: 006d1bd8
Run Code Online (Sandbox Code Playgroud)
确认两者都指向同一张表.手册陈述,而不是多次保证,_ENV并且_G只是普通的名称,没有隐藏的意义,Lua本身不在内部使用它.我在下面尝试了这个块:
local a = { }
local b = a -- since tables are objects, both refer to the same table object
print(a, b) -- same address printed twice
a = { } -- point one of them to a newly constructed table
print(a, b) -- new, old table addresses printed
Run Code Online (Sandbox Code Playgroud)
现在_G和_ENV以下一样做:
local g = _G -- make an additional reference
print(g, _G, _ENV) -- prints same address thrice
local p = print -- backup print for later use
_ENV = { } -- point _ENV to a new table/environment
p(g, _G, _ENV) -- old, nil, new
table: 00ce1be0 table: 00ce1be0 table: 00ce1be0
table: 00ce1be0 nil table: 00ce96e0
Run Code Online (Sandbox Code Playgroud)
如果_G是一个普通的全球化,为什么它会变成nil这里?如果完成引用计数_G,则在_ENV释放它时仍然保持引用.像b上面一样,它也应该坚持旧桌子,不是吗?
但是,对于下面的块,_G不变/保留!
_ENV = { _G = _G }
_G.print(_G, _ENV, _ENV._G) -- old, new, old
Run Code Online (Sandbox Code Playgroud)
但在这里它被杀死了:
_ENV = { g = _G }
_ENV.g.print(_ENV, _ENV.g, _G) -- new, old, nil
Run Code Online (Sandbox Code Playgroud)
保留它的另一种情况:
print(_G, _ENV) -- print same address twice
local newgt = {} -- create new environment
setmetatable(newgt, {__index = _G}) -- set metatable with _G as __index metamethod
_ENV = newgt -- point _ENV to newgt
print(_G, newgt, _ENV) -- old, new, new
Run Code Online (Sandbox Code Playgroud)
由于行为有如此多的变化,_G手册给出的原始保证似乎不稳定.我在这里错过了什么?
_G?它有三种特殊之处:
lua_State不打开"基础"模块的情况下创建新的,则不会有_G变量.但是,独立解释器已经加载了所有标准库._G,更改/删除它可能会破坏这些模块._G?Lua中的全局变量使用普通表实现.对非local变量或upvalue 的变量的任何访问都将重定向到此表.局部变量始终具有优先权,因此如果您有一个全局变量和一个具有相同名称的局部变量,您将始终获得本地变量.这里_G发挥作用:如果你想要全局变量,你可以说_G.name而不是name.假设名称_G不是局部变量(它是为Lua保留的,请记住?!),这将始终通过使用表索引语法获取全局变量的值,从而消除局部变量名称的歧义.在较新的Lua版本(5.2+)中,您也可以使用它
_ENV.name作为替代方案,但是在_G这些版本之前并保持兼容性.
在其他情况下,您希望获得全局表格,例如设置元表.Lua允许您通过使用该setmetatable函数设置元表来自定义表(和其他值)的行为
,但您必须以某种方式将表作为参数传递._G帮助你做到这一点.
如果已在globals表中添加了metatable,在某些情况下,您可能想要规避刚刚安装的元方法(__index和/或
__newindex).您可以使用rawget和
rawset,但是您还需要将globals表作为参数传递.
请注意,上面列出的所有用例仅适用于Lua代码而不适用于
C代码.在C代码中,您没有命名的局部变量,只有堆栈索引.所以没有歧义.如果你想将globals表的引用传递给某个函数,你可以使用
lua_pushglobaltable(它使用注册表而不是_G).因此,用C实现的模块不使用/需要_G
全局变量.这适用于Lua的标准库(在C中实现).事实上,参考手册
的保证,即_G(变量,而不是表)不使用的Lua或它的标准库.
_G涉及_ENV?从版本5.0开始,Lua允许您更改用于在每个(Lua)函数的基础上查找全局变量的表.在Lua 5.0和5.1中你使用了setfenv函数(globals表也称为"函数环境",因此名称setfenv).Lua 5.2使用另一个特殊变量名引入了一种新方法_ENV.
_ENV虽然不是一个全局变量,Lua确保每个块都以一个_ENVupvalue开头.新方法的工作原理是让Lua将对非本地(和非upvalue)变量名称的任何访问权转换a为_ENV.a.无论_ENV在代码中的那一点,用于解决全局变量.这种方式更安全,因为您无法更改自己未编写的代码环境(不使用调试库),也更灵活,因为您可以通过创建local _ENV有限的变量来更改单个代码块的环境
范围.
但是,在任何情况下,您都需要在程序员有机会设置自定义环境之前使用的默认环境(或者如果程序员不想更改它).在启动时,Lua会为您创建此默认环境(也称为"全局环境")并将其存储在注册表中._ENV除非您将自定义环境传递给load或,否则此默认环境将用作所有块的
upvalue
loadfile.lua_pushglobaltable还可以直接从注册表中检索此全局环境,因此所有C模块都会自动使用它来访问全局变量.
而如果标准的"基地" C模块已经被加载,这个默认的"全球环境"有一个叫做表字段_G回指向全球环境.
_G实际上是全局变量_ENV._G._ENV 不是全局变量,而是upvalue变量或局部变量._G默认"全局环境" 的字段指向全局环境._G并且_ENV默认情况下引用相同的表(所述全局环境)._G(在全局环境中)而不破坏C模块或Lua本身(但如果不小心,您可能会破坏第三方Lua模块)._ENV,因为它只影响您自己的代码(最多当前的块/文件)._ENV,您可以自行决定是否_G
(_ENV._G)将在受影响的代码中可用,以及它指向的内容.| 归档时间: |
|
| 查看次数: |
6866 次 |
| 最近记录: |