• 对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在云风的博客有提及。

  • 相关阅读:
    百度地图js lite api 支持点聚合
    看源码积累知识点
    React 16 源码瞎几把解读 【三 点 二】 react中的fiberRoot
    React 16 源码瞎几把解读 【三 点 一】 把react组件对象弄到dom中去(矛头指向fiber,fiber不解读这个过程也不知道)
    React 16 源码瞎几把解读 【二】 react组件的解析过程
    获得BAT技术专家Offer,他到底做了什么?
    Android 日常开发总结的技术经验
    理解Android虚拟机体系结构
    Android开发人员应该选择哪种语言?
    2019年Android岗位BAT等大厂面试题,希望对新的一年的你有所帮助
  • 原文地址:https://www.cnblogs.com/yuyaonorthroad/p/6337506.html
Copyright © 2020-2023  润新知