函数 load
通常把被加载代码段的上值 _ENV
初始化为全局变量。不过,函数 load
还有一个可选的第四个参数来让我们为 _ENV
指定一个不同的初始值(函数 loadfile
也有一个类似的参数)。
例如,加入我们有一个典型的配置文件,该配置文件定义了程序要使用的几个常量和函数,如下:
-- 文件'config.lua' width = 200 height = 300 ...
可以使用如下的代码加载该文件:
env = {} loadfile("config.lua", "t", env)()
配置文件中的所有代码会运行在空的环境 env
中,类似于某种沙盒。特别地,所有的定义都会进入这个环境中。即使出错,配置文件也无法影响任何别的东西,甚至是恶意的代码也不能对其他东西造成任何破坏。除了通过消耗 CPU
时间和内存来制造拒绝服务(Denial of Service,DOS)攻击,恶意代码也做不了什么其他的事。
有时,我们可能像重复运行一段代码数次,每一次使用一个不同的环境。在这种情况下,函数 load
可选的参数就没有用了。此时,我们有另外两种选择。
第一种选择是使用调试库中的函数 debug.setupvalue
。顾名思义,函数 setupvalue
允许改变任何指定函数的上值,例如:
f = load("b = 10; return a") env = {a = 20} debug.setupvalue(f 1, env) print(f()) --> 20 print(env.b) --> 10
setupvalue
的第一个参数是指定的函数,第二个参数是上值的索引,第三个参数是新的上值。对于这种用法,第二个参数永远是 1
:当函数表示的是一段代码时, Lua
语言可以保证它只有一个上值且上值就是 _ENV
。
这种方式的一个小缺点在于依赖调试库。调试库打破了有关程序的一些常见假设。例如, debug.setupvalue
打破了 Lua
语言的可见性规则,而可见性规则可以保证我们不能从词法定界的范围外访问局部变量。
另一种在几个不同环境中运行代码的方式是每次加载代码段时稍微对其进行一下修改。假设我们在要加载的代码段前加入一行:
_ENV = ...;
注意
由于 Lua
语言把所有的代码段都当做可变长参数函数进行编译,因此,多出的这一行代码会把传给代码段的第一个参数赋值给 _ENV
,从而把参数设为环境。