环境教程 |
|
默认全局表存储在它本身的 "_G"
键下,如果你想获得它的实际引用,这很有用。
Lua 5.2 中的环境工作方式与 5.1 大不相同。这里将解释两种方式。
函数的环境存储在一个名为 _ENV
的上值中。例如,以下是一个将环境设置为自定义环境并使用其中变量的函数
print(_ENV == _G) -- prints true, since the default _ENV is set to the global table a = 1 local function f(t) local print = print -- since we will change the environment, standard functions will not be visible local _ENV = t -- change the environment. without the local, this would change the environment for the entire chunk print(getmetatable) -- prints nil, since global variables (including the standard functions) are not in the new env a = 2 -- create a new entry in t, doesn't touch the original "a" global b = 3 -- create a new entry in t end local t = {} f(t) print(a, b) --> 1 nil print(t.a, t.b) --> 2 3
加载代码块时,顶层函数会获得一个新的 _ENV 上值,并且它内部的任何嵌套函数都可以看到它。你可以假装加载过程类似于以下方式
local _ENV = _G return function (...) -- this function is what's returned from load -- code you passed to load goes here, with all global variable names replaced with _ENV lookups -- so, for example "a = b" becomes "_ENV.a = _ENV.b" if neither a nor b were declared local end
在大多数情况下,你不需要使用环境,除非你想沙盒化一个加载的代码块,以便通过使其看起来像全局变量来方便地访问某些函数,或者出于安全原因阻止它看到不安全的函数。这就是为什么 5.2 的 load 函数接受一个参数,允许你将代码块的 _ENV 设置为自定义表,而不是 _G。
local sandbox_env = { print = print, } local chunk = load("print('inside sandbox'); os.execute('echo unsafe')", "sandbox string", "bt", sandbox_env) chunk() -- prevents os.execute from being called, instead raises an error saying that os is nil
如果你真的想创建一个沙盒来运行不可信代码,请记住,很容易忽略许多可以被利用的东西,并且你需要某种方法来限制 CPU 使用率和内存。
在 Lua 5.1 中,环境是它们自己的东西,与局部变量或上值无关。相反,每个函数都有一个与之关联的环境表,可以使用 getfenv
/setfenv
标准函数来操作它。
getfenv
和 setfenv
都接受一个函数或堆栈级别(其中 1 是当前函数,2 是调用当前函数的函数,等等)。setfenv
有一个第二个参数,它接受新的 env 表,而 getfenv
返回函数的当前 env 表。
为 5.1 重写的先前示例
print(getfenv(1) == _G) -- prints true, since the default env is set to the global table a = 1 local function f(t) local print = print -- since we will change the environment, standard functions will not be visible setfenv(1, t) -- change the environment print(getmetatable) -- prints nil, since global variables (including the standard functions) are not in the new env a = 2 -- create a new entry in t, doesn't touch the original "a" global b = 3 -- create a new entry in t end local t = {} f(t) print(a, b) --> 1 nil print(t.a, t.b) --> 2 3
为 5.1 重写的沙盒示例
local sandbox_env = { print = print, } local chunk = loadstring("print('inside sandbox'); os.execute('echo unsafe')") setfenv(chunk, sandbox_env) chunk() -- prevents os.execute from being called, instead raises an error saying that os is nil
5.1 的方式有时被认为更简单、更通用,但它也需要对环境进行特殊处理(而不是使用现有的局部变量系统)。此外,5.2 的方式的设计理念是函数的环境应该是私有的,而不是在没有调试库的情况下从任何地方都可以访问,因此它可以被认为更安全。