背景知识
Lua 给我的感觉是:各种内置函数和标准库的存在感都是比较强的。如果执行这句:
for name in pairs(_G) do print(_G) end
就会把各种环境中已存在名称的打印出来:
- 全局变量:比如字符串
_VERSION
- 。
- 内置函数:比如
print
- 、
tonumber
- 、
dofile
- 模块名称:比如
string
- 、
io
- 、
coroutine
_G
就是存放环境的表(于是会有 _G
中存在着 _G._G
_G
value = _G[varname] --> value = varname
_G[varname] = value --> varname = value
改变函数的环境
setfenv(f, table)
函数改变,其中 table
是新的环境表,f
表示需要被改变环境的函数。如果 f
a = 3 -- 全局变量 a
setfenv(1, {}) -- 将当前函数的环境表改为空表
print(a) -- 出错,因为当前环境表中 print 已经不存在了
a
不存在,连 print
都一块儿不存在了。如果需要引用以前的 print
a = 3
setfenv(1, { g = _G })
g.print(a) -- 输出 nil
g.print(g.a) -- 输出 3
沙盒
setfenv
local env = {} -- 沙盒环境表,按需要添入允许的函数
function run_sandbox(code)
local func, message = loadstring(code)
if not func then return nil, message end -- 传入代码本身错误
setfenv(func, env)
return pcall(func)
end
Lua 5.2 的 _ENV 变量
var
的访问都会在语法上翻译为 _ENV.var
。而 _ENV
本身被认为是处于当前块外的一个局部变量。(于是只要你自己定义一个名为 _ENV
的变量,就自动成为了其后代码所处的「环境」(enviroment)。另有一个「全局环境」(global enviroment)的概念,指初始的 _G
Lua 的作者之一 Roberto Ierusalimschy 同志在介绍 Lua 5.2 时说:
the new scheme, with _ENV, allows the main benefit of setfenv with a little more than syntactic sugar.
setfenv
、getfenv
访问的所谓「环境」终于实体化为一个始终存在的变量 _ENV
了。
于是以下两个函数内容大致是一样的:
-- Lua 5.1
function foobar()
setfenv(1, {})
-- code here
end
-- Lua 5.2
function foobar()
local _ENV = {}
-- code here
end
load
函数作出了修改。(包括但不限于 :))合并了 loadstring
local func, message = load(code, nil, "t", env)