对lua中_ENV表的理解(lua5.2版本以后)

  当我拿到_ENV表的时候,会去想这个_ENV表是干什么用的? 首先看如下代码:

1 print(_ENV) --0x1d005f0
2 print(_G)   --0x1d005f0
ViewCode

  看了上面的代码,就感觉_ENV表不就是_G表吗?但_ENV表是不是全局的呢?我又打印了_G表的内容:

 1 for k , v in pairs(_G) do
 2     print(k , v)
 3 end
 4 --[[
 5 package    table: 0xad1e50
 6 setmetatable    function: 0x419220
 7 pairs    function: 0x419380
 8 require    function: 0xad3900
 9 loadfile    function: 0x419540
10 print    function: 0x418ce0
11 module    function: 0xad3890
12 rawlen    function: 0x418c50
13 load    function: 0x419430
14 getmetatable    function: 0x4195b0
15 type    function: 0x418800
16 coroutine    table: 0xad3970
17 table    table: 0xad3d10
18 error    function: 0x418f40
19 _VERSION    Lua 5.2
20 debug    table: 0xad4bb0
21 string    table: 0xad2700
22 rawequal    function: 0x418ca0
23 math    table: 0xad64d0
24 tonumber    function: 0x418870
25 bit32    table: 0xad2d60
26 os    table: 0xad3c60
27 loadstring    function: 0x419430
28 pcall    function: 0x4191c0
29 io    table: 0xad4030
30 select    function: 0x418aa0
31 unpack    function: 0x41fb40
32 collectgarbage    function: 0x418fb0
33 xpcall    function: 0x419110
34 rawset    function: 0x418bb0
35 ipairs    function: 0x4193a0
36 next    function: 0x418e20
37 rawget    function: 0x418c00
38 tostring    function: 0x418840
39 arg    table: 0xad76a0
40 _G    table: 0xad15f0
41 assert    function: 0x419680
42 dofile    function: 0x419600
43 ]]
View Code

  发现_G表中的Key是没有_ENV表的,就比较疑惑?那_ENV到是什么,是怎么样产生的?关于这两个问题我看了lua闭包

的源码,lua闭包有两种生成方式,其中一种是在lua源码加载时,会生成闭包(ps:关于闭包的整体内容会在之后的另一篇博客讲),

对于该闭包它的第一个Upvalue就是_ENV,具体代码如下:

 1 LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data,
 2                       const char *chunkname, const char *mode) {
 3   ZIO z;
 4   int status;
 5   lua_lock(L);
 6   if (!chunkname) chunkname = "?";
 7   luaZ_init(L, &z, reader, data);
 8   status = luaD_protectedparser(L, &z, chunkname, mode);
 9   if (status == LUA_OK) {  /* no errors? */
10     LClosure *f = clLvalue(L->top - 1);  /* get newly created function */
11     if (f->nupvalues >= 1) {  /* does it have an upvalue? */
12       /* get global table from registry */
13       Table *reg = hvalue(&G(L)->l_registry);
14       const TValue *gt = luaH_getint(reg, LUA_RIDX_GLOBALS);
15       /* set global table as 1st upvalue of 'f' (may be LUA_ENV) */
16       setobj(L, f->upvals[0]->v, gt);
17       luaC_upvalbarrier(L, f->upvals[0]);
18     }
19   }
20   lua_unlock(L);
21   return status;
22 }
lua_load中将注册表中的全局表(_G赋值给_ENV)

  所以_ENV默认会指向_G表。至于_G表是如何创建的,各位可以参考这个博客。那_ENV表的作用是什么呢?

  _ENV表的作用:表示当前代码块的环境,每一个代码块的环境都有一张_ENV。关于代码块的解释可以看这里。我们可以看一下下面这段代码就理解了:

 1 function foorbar(env)
 2     local _ENV = env
 3     return function() print("yes") end
 4 end
 5 
 6 print(_ENV)
 7 
 8 local l_test = foorbar({})
 9 
10 print(l_test())
11 
12 --[[
13 table: 0x1a395f0
14 lua: code:3: attempt to call global 'print' (a nil value)
15 stack traceback:
16     code:3: in function 'l_test'
17     code:10: in main chunk
18     [C]: in ?
19 ]]
View Code

  上述可以看出,我们的所有内部函数都定义与一张_ENV表中,比如在print时实际上是_ENV.print(语法糖省略了),所以在代码块foorbar中_ENV被赋值为{},所以print就访问不到了。

所以_ENV表的作用实际上是充当环境,所以在5.2之后就没有全局变量这样一个说法(_ENV = _G还是有全局变量的)。这种做法类似于5.1的setfenv。关于如何实现setfenv在云风的博客有提及。

 

posted on 2017-01-21 18:37  余姚北路  阅读(1326)  评论(0编辑  收藏  举报