当我拿到_ENV表的时候,会去想这个_ENV表是干什么用的? 首先看如下代码:
1 print(_ENV) --0x1d005f0 2 print(_G) --0x1d005f0
看了上面的代码,就感觉_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 ]]
发现_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 }
所以_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 ]]
上述可以看出,我们的所有内部函数都定义与一张_ENV表中,比如在print时实际上是_ENV.print(语法糖省略了),所以在代码块foorbar中_ENV被赋值为{},所以print就访问不到了。
所以_ENV表的作用实际上是充当环境,所以在5.2之后就没有全局变量这样一个说法(_ENV = _G还是有全局变量的)。这种做法类似于5.1的setfenv。关于如何实现setfenv在云风的博客有提及。