使用 _ENV

简介: 使用 _ENV

提示

本篇笔记中的大部分示例必须以单段代码的方式运行。如果在交互模式下一行一行的输入代码,那么每一行代码都会变成一段独立的代码,因此每一行都会有一个不同的 _ENV 变量。为了把代码当做一个代码段运行,要么把代码保存在一个文件中运行,要么使用 do-end 将代码段包围起来。


由于 _ENV 只是一个普通的变量,因此可以对其赋值或像访问其他变量一样访问它。赋值语句 _ENV = nil 会使得后续代码不能直接访问全局变量。这可以用来控制代码使用那种变量:

local print, sin = print, math.sin
_ENV = nil
print(13)             --> 13
print(sin(13))        --> 0.42016703682664
print(math.cos(13))   --> ERROR: attempt to index a nil value (upvalue '_ENV')点击复制复制失败已复制


任何对自由名称("全局变量")的赋值都会引发类似的错误。


我们可以显式地使用 _ENV 来绕过局部声明:

a = 13            -- 全局的
local a = 12
print(a)          --> 12  (局部的)
print(_ENV.a)     --> 13  (全局的)


_G 也可以:

a = 13            -- 全局的
local a = 12
print(a)          --> 12  (局部的)
print(_G_.a)      --> 13  (全局的)


通常, _G_ENV 指向的是同一个表。但是,尽管如此,他们是很不一样的实体。 _ENV 是一个局部变量,所有对"全局变量"的访问实际上访问的都是 _ENV_G 则是一个在任何情况下都没有任何特殊状态的全局变量。按照定义, _ENV 永远指向的是当前的环境;而假设在可见且无人改变过其值的前提下, _G 通常指向的是全局环境。

_ENV 的主要用途是用来改变代码段使用的环境。一旦改变了环境,所有的全局访问就都将使用新表:

-- 将当前的环境改为一个新的空表
_ENV = {}
a = 1         -- 在_ENV中创建字段
print(a)      --> attempt to call a nil value (global 'print')


如果新环境是空的,就会丢失所有的全局变量,包括函数 print 。因此,应该首先把一些有用的值放入新环境,比如全局环境:

a = 15                -- 创建一个全局变量
_ENV = {g = _G}       -- 改变当前环境
a = 1                 -- 在_ENV中创建一个字段
g.print(_ENV.a, g.a)  --> 1   15


这时,当访问"全局"的 g (位于 _ENV 而不是全局环境中)时,我们使用的是全局环境,在其中能够找到函数 print


我们可以使用 _G 代替 g ,从而重写前面的例子:

a = 15                        -- 创建一个全局变量
_ENV = {_G = _G}              -- 改变当前环境
a = 1                         -- 在_ENV中创建一个字段
_G.print(_ENV.a, _G.a)        --> 1     15


_G 只有在 Lua 语言创建初始化的全局表并让字段 _G 指向它自己的时候,才会出现特殊状态。 Lua 语言并不关心该变量的当前值。不过尽管如此,就像我们在上面重写的示例中所看到的那样,将指向全局环境的变量命名为同一个名字( _G )是一个惯例。


另一种把旧环境装入新环境的方式是使用继承:

a = 1
local newgt = {}                              -- 创建新环境
setmetatable(newgt, {__index = _G})
_ENV = newgt                                  -- 设置新环境
print(a)                                      --> 1


在这段代码中,新环境从全局环境中继承了函数 printa 。不过,任何赋值都会发生在新表中。虽然我们仍然能通过 _G 来修改全局环境中的变量,但如果误改了全局环境中的变量也不会有什么影响。

a = 1
local newgt = {}                              -- 创建新环境
setmetatable(newgt, {__index = _G})
_ENV = newgt                                  -- 设置新环境
print(a)                                      --> 1
a = 10
print(a, _G.a)                                --> 10    1
_G.a = 20
print(_G.a)                                   --> 20


作为一个普通的变量, _ENV 遵循通常的定界规则。特别地,在一段代码中定义的函数可以按照访问其他外部变量一样的规则访问 _ENV

_ENV = {_G = _G}
local function foo ()
  _G.print(a)
end
a = 10
foo()                       --> 10
_ENV = {_G = _G, a = 20}
foo()                       --> 20


如果定义一个名为 _ENV 的局部变量,那么对自由名称的引用将会绑定到这个新变量上:

a = 2
do 
  local _ENV = {print = print, a = 14}
  print(a)                                  --> 14
end
print(a)                                    --> 2   (回到原始的_ENV中)点击复制复制失败已复制


因此,可以很容易地使用私有环境定义一个函数:

function factory (_ENV)
  return function () return a end
end
f1 = factory{a = 6}
f2 = factory{a = 7}
print(f1())                 --> 6
print(f2())                 --> 7


factory 函数创建了一个简单的闭包,这个闭包返回了其中"全局"的 a 。每当闭包被创建时,闭包可见的变量 _ENV 就成了外部 factory 函数的参数 _ENV 。因此,每个闭包都会使用自己对外部变量(作为上值)来访问其自由名称。


使用普通的定界规则,我们可以有几种方式操作环境。例如,可以让多个函数共享一个公共环境,或者让一个函数改变它与其他函数共享的环境。

目录
相关文章
|
7月前
|
JavaScript
Vue中 .env .env.development .env.production 详细说明
Vue中 .env .env.development .env.production 详细说明
315 0
|
7月前
|
存储 JavaScript 前端开发
.env.development是什么
.env.development是什么
249 0
php案例:$_ENV的数据怎么样才能显示出来.$_ENV的简单运用
php案例:$_ENV的数据怎么样才能显示出来.$_ENV的简单运用
php案例:$_ENV的数据怎么样才能显示出来.$_ENV的简单运用
|
7月前
|
JavaScript 测试技术
vue环境变量配置——process.env(详细)
vue环境变量配置——process.env(详细)
233 0
|
JavaScript 测试技术
vue环境变量配置——process.env
vue环境变量配置——process.env
138 0
|
数据安全/隐私保护 Docker 容器
45-Dockerfile-ARG/ENV指令
45-Dockerfile-ARG/ENV指令
|
JavaScript 测试技术
env文件里小心$
env文件里小心$
120 0
env->FindClass()为NULL的一种解决办法
env->FindClass()为NULL的一种解决办法
170 0
|
自然语言处理 索引
_ENV 和 load
_ENV 和 load
125 0
|
JavaScript
process.env 是什么?
process对象是一个全局变量,提供了有关当前 Node.js 进程的信息并对其进行控制。 作为全局变量,它始终可供 Node.js 应用程序使用,无需使用 require()。 它也可以使用 require() 显式地访问。
243 0