5.1之前, 全局变量存储在_G这个table中, 这样的操作:
a = 1 
相当于:
_G['a'] = 1

但在5.2之后, 引入了_ENV叫做环境,与_G全局变量表产生了一些混淆,需要从原理上做一个理解。
在5.2中, 
操作a = 1
相当于
_ENV['a'] = 1
这是一个最基础的认知改变,其次要格外注意_ENV不是全局变量,而是一个upvalue(非局部变量)。

其次,_ENV['_G']指向了_ENV自身,这一目的是为了兼容5.1之前的版本,因为之前你也许会用到:

_G['a'] = 2 , 在5.2中, 这相当于_ENV['_G']['a'],为了避免5.1之前的老代码在5.2中运行错误,所以5.2设置了_ENV['_G']=_ENV来兼容这个问题。然而你不要忘记_ENV['_G']=_ENV,所以一切都顺理成章了。

在5.1中,我们可以为一段代码块(或者函数)设置环境,使用函数setfuncs,这样会导致那一段代码/函数访问全局变量的时候使用了setfuncs指定的table,而不是全局的_G。

在5.2中,setfuncs遭到了废弃,因为引入了_ENV。 通过在函数定义前覆盖_ENV变量即可为函数定义设置一个全新的环境,比如:
a = 3
function get_echo()
local _ENV={print=print, a = 2}
return function echo()
print(a)
end
end

get_echo()()
会打印2,而不是3,因为echo函数的环境被修改为{print=print, a=2},而print(a)相当于访问_ENV['a'](先忘掉那为了兼容而存在的_G)。

这就是_ENV的基本用法了。

另外,不得不提到lua的C支持中关于全局变量与环境的细节,只能简单描述,你必须自己试试才能记得清楚。

lua_setglobal/lua_getglobal都是操作lua_State注册表中LUA_RIDX_GLOBALS伪索引指向的全局变量表,与lua中访问_ENV['a']或者a是不同的。

lua_load加载lua代码后会返回一个函数,默认会给这个函数设置一个upvalue就叫_ENV,起值是LUA_RIDX_GLOBALS的全局变量表,你可以lua_setupvalue设置这个函数的upvalue,即下标1的upvalue,因为这个位置是这个函数的_ENV表存放位置(你可以通过lua_setupvalue的返回值印证这一点)

这里巧妙的是,lua_State会在创建时保证LUA_RIDX_GLOBALS的全局变量表中包含一个指向自己的_G元素,这样就保证了在不调用lua_setupvalue的情况下该返回函数的_ENV['_G']是指向自己的,即LUA_RIDX_GLOBALS这个全局表。(其实你的lua解释器就是简单的lua_load后pcall的,对于一个刚启动lua_State来说是没有_ENV的,是lua解释器load你的代码时自动给带上的_ENV,其值是lua_state的LUA_RIDX_GLOBALS全局表。)

一些有意思的东西是需要你自己摸索的,lua语言自身就很简练,并且所有东西都不是什么神秘的事情,可以通过读源码或者试验摸索得到。

最后,提一下,lua_state启动后在注册表里LUA_RIDX_GLOBALS下标存放的全局表一定有一个元素是指向自己的,即_G.

任何的全局变量名 var 在语法分析的时候都会被翻译为 _ENV .var的形式,这个会在 3.2 和 3.3.3 时再讨论。每一个代码块在它的作用域内都有一个叫 _ENV 的外部局部变量(参考

3.3.2),所以_ENV 在代码块里也不是一个全局变量。
虽然_ENV 看起来像是编译器生成的内容,但它确是实实在在存在的。你可以直接 在

Lua 的代码中使用_ENV 来定义一个新的变量和参数。每一个通过 _ENV 引用的全局名字,
它在程序中是被全局可见的,它遵守 Lua 的可见性规则(参考 3.5)。

任何一个用法如_ENV 一样的 table 都称为环境。

Lua 中保留了一个特殊的全局环境 。它的值被保留在 Lua 栈中,通过一个特殊的索引值
来访问(参考 4.5)。在 Lua 的代码中,变量_G 指向这个值。

当编译一个代码块时 ,Lua 会把全局环境中的内容作为数据来初始化 _ENV(参考 load)。
因此,Lua 代码中的全局变量指的是全局环境中的内容 。此外,所有标准库中的函数都会被
加载进全局环境中,同时也提供了一些操作这个环境的函数 。你可以使用 load(或者 loadfile)
在加载一个代码块时指定不同的环境 。(在 C 代码中,你必须在加载代码块后通过改变第一
个上值(upvalue)来实现环境的改变 。)

如果你改变了 Lua 注册表中的全局环境(通过 C 代码或调试库),那么之后加载的代码
块将会得到一个新的环境。之前加载的代码块不受影响,它仍然使用之前的 _ENV 变量。此
外,变量_G(储存在原先的全局环境中)从来不会被 Lua 更新

1._ENV是一个外部全局变量

2.

_ENV和_G的更多相关文章

  1. Lua5.3 注册表 _G _ENV

    Lua5.3 注册表 _G _ENV 来源:http://blog.csdn.net/murisly/article/details/46518551 注册表的描述,借用PIL中的一段话: regis ...

  2. Chapter 14_4 使用_ENV

    因为_ENV是一个普通的变量,我们可以像其他变量一样去对它进行赋值和访问. _ENV = nil 上面的赋值操作,将会使得在它之后的代码块不能直接访问全局变量.不过,对控制你的代码所使用的变量有用处. ...

  3. [转]http://lua-users.org/wiki/LpegTutorial

    Simple Matching LPeg is a powerful notation for matching text data, which is more capable than Lua s ...

  4. [翻译]lpeg入门教程

    原文地址:http://lua-users.org/wiki/LpegTutorial 简单匹配 LPeg是一个用于文本匹配的有力表达方式,比Lua原生的字符串匹配和标准正则表达式更优异.但是,就像其 ...

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

    当我拿到_ENV表的时候,会去想这个_ENV表是干什么用的? 首先看如下代码: print(_ENV) --0x1d005f0 print(_G) --0x1d005f0 ViewCode 看了上面的 ...

  6. discuz二次开发笔记(一)------$_G全解析

    $_G 保存了 Discuz! 中所有的预处理数据缓存能够很好的提高程序的性能,一些配置数据没必要每次都查询数据库,只要在修改了的时候更新下缓存即可.Discuz! 中所有的缓存保存在 $_G[cac ...

  7. Chapter 14_5 _ENV和load

    load在加载代码块时,通常使用全局环境去初始化代码块的_ENV上值. 然而,load选项的第四个参数给出的值就是供_ENV用的.(loadfile函数参数也是一样) 举一个有关初始化的例子,假如我们 ...

  8. tp5 $_ENV获取不到数据

    $_ENV变量是取决于服务器的环境变量的,从不同的服务器上获取的$_ENV变量打印出的结果可能是不同的. php的配置文件php.ini的配置项为:variables_order = "GP ...

  9. discuz $_G变量

    class.core.php中 global $_G;        $_G = array(            'uid' => 0,            'username' => ...

随机推荐

  1. Delphi 7调用C语言编写的DLL

    DLL一定是要C语言导出的符号,也就是extern “C”. 当然,我们都知道DLL调用分为动态调用和静态调用. 动态调用的一般思想为,先LoadLibrary那个你想加载的DLL,然后通过GetPr ...

  2. OpenGL实现通用GPU计算概述

    可能比較早一点做GPU计算的开发者会对OpenGL做通用GPU计算,随着GPU计算技术的兴起,越来越多的技术出现,比方OpenCL.CUDA.OpenAcc等,这些都是专门用来做并行计算的标准或者说接 ...

  3. 有效利用番茄工作法提高效率--XorTime的使用方法

      之前下载过番茄工作法XorTime,但是一直不知道怎么用,我想很多朋友应该也会遇到这种情况,于是我通过网上查找并直接总结,把XorTime的使用方法给写下,希望能够帮到更多的朋友..        ...

  4. jenkins shell脚本自动化构建阿里云k8s上应用

    #!/bin/bash source /etc/profile cd /mnt/workspace/weifeng-ui-pre/ npm i && npm run build rm ...

  5. Alias Method for Sampling 采样方法

    [Alias Method for Sampling]原理 对于处理离散分布的随机变量的取样问题,Alias Method for Sampling 是一种很高效的方式. 在初始好之后,每次取样的复杂 ...

  6. linux shell 脚本攻略学习9--rename命令详解

    rename命令详解: 对文件重命名是常用的操作之一,一般对单个文件的重命名用mv命令,如: amosli@amosli-pc:~/learn/example$ ls abc.txt amosli@a ...

  7. Xcode 常用调试技巧总结

    NSLog,po命令和普通断点调试相信每个iOS开发者都会,这里就不作介绍了. 一.Memory Graph Xcode8新增:Memory Graph解决闭包引用循环问题 有很多叹号说明就有问题了. ...

  8. 【Spring】SpringMVC之拦截器

    Spring的HandlerMapping处理器支持拦截器应用.当需要为某些请求提供特殊功能时,例如实现对用户进行身份认证.登录检查等功能. 拦截器必须实现HandlerInterceptor接口,实 ...

  9. 【Oracle】Oracle索引

    在关系数据库中,索引是一种与表有关的数据库结构,它可以使对应于表的SQL语句执行得更快.索引的作用相当于图书的目录,可以根据目录中的页码快速找到所需的内容. 对于数据库来说,索引是一个必选项,但对于现 ...

  10. 【java基础】Java反射机制

    一.预先需要掌握的知识(java虚拟机)  1)java虚拟机的方法区:  java虚拟机有一个运行时数据区,这个数据区又被分为方法区,堆区和栈区,我们这里需要了解的主要是方法区.方法区的主要作用是存 ...