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. Android Studio 错误: 非法字符: '\ufeff' 解决方式|错误: 须要class, interface或enum

    在导入eclipse项目到Android Studio出现这种错误, 非法字符: '\ufeff' 解决方式|错误: 须要class, interface或enum.查阅后了解到Eclipse能够智能 ...

  2. Receiver not registered.

    1. 在MessageListActivity中出现异常:Receiver not registered. 07-16 11:15:47.881: E/AndroidRuntime(32361): F ...

  3. Asp.net中GridView使用详解(引)【转】

    Asp.net中GridView使用详解(引) GridView无代码分页排序 GridView选中,编辑,取消,删除 GridView正反双向排序 GridView和下拉菜单DropDownList ...

  4. JavaScript插件:快速选择日期----jedate日历插件

    jedate是一款轻量级JS库,可快速选择日期 http://www.sucaijiayuan.com/Js/DateTime/1371.html 使用方法: 下载jedate , 解压后不要改变文件 ...

  5. HDU 1258 Sum It Up (DFS)

    Sum It Up Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total S ...

  6. C#中缓存的简单方法及使用Sql设置缓存依赖项

    概述 使用Cache高速缓存可以提高数据的读取速度,减少服务器与客户端之间的数据交互.因为Cache一经创建就会占用服务器上的资源,所以Cache并不是越多越好,一般用于数据较固定,使用较频繁的地方. ...

  7. bootstrap-datepicker限定可选时间范围

    此项目是 bootstrap-datetimepicker 项目 的一个分支,原项目不支持  Time 选择. 其它部分也进行了改进.增强,例如 load 过程增加了对 ISO-8601 日期格式的支 ...

  8. .NET跨平台实践:用C#开发Linux守护进程-Daemon

    Linux守护进程(Daemon)是Linux的后台服务进程,它脱离了与控制终端的关联,直接由Linux init进程管理其生命周期,即使你关闭了控制台,daemon也能在后台正常工作. 一句话,为L ...

  9. top 学习

    通常top命令是会持续运行而不终止的. 要在脚本里用,需要添加一些选项参数,尤其是-b.例如:top -b -n 2 -d 3 >/tmp/log -b表示批处理模式(Batch mode),以 ...

  10. 面试必备:HashMap源码解析(JDK8)

    1 概述 本文将从几个常用方法下手,来阅读HashMap的源码. 按照从构造方法->常用API(增.删.改.查)的顺序来阅读源码,并会讲解阅读方法中涉及的一些变量的意义.了解HashMap的特点 ...