当您创建或使用全局变量时,它实际上存储在名为“环境”的表中,或 _ENV(它不是全局的,它是您自动获得的局部变量)
所以你的代码:
function foo()
return "Hello,"
end
function bar()
return " World!"
end
hello = foo()..bar()
真的在做:
function _ENV.foo()
return "Hello,"
end
function _ENV.bar()
return " World!"
end
_ENV.hello = _ENV.foo().._ENV.bar()
所以如果我们只是将_ENV 设置为一个新表并将其返回,那么这正是您想要的表。您可以通过将load(这是您运行字符串的方式)作为第四个参数传递来执行此操作。这个函数会这样做:
function loadAndReturnEnv(codeString)
local env = {}
load(codeString, nil, nil, env)() -- error checking skipped for demonstration
return env
end
但是,请注意,所有常见的全局内容,例如 string.rep、table.sort、load、print,在这个新的 _ENV 中都不存在。如果这就是你想要的,那就太好了。但我猜你可能确实想要它们。在这种情况下,我们可以使用元表 __index 功能,这样如果代码在其 _ENV 中查找某些内容但它不存在,则它会在我们(调用者的)_ENV 中查找。
function loadAndReturnEnvWithGlobals(codeString)
-- note: env is the environment for the code we're about to run
-- (which will be called _ENV in that code), and _ENV is the environment
-- of the loadAndReturnEnvWithGlobals function (i.e. the "real" environment)
local env = {}
setmetatable(env, {__index=_ENV})
load(codeString, nil, nil, env)() -- error checking skipped for demonstration
setmetatable(env, nil) -- set the table back to normal before we return it
return env
end
但是等等...程序现在可以在运行时调用print,但是如果它定义了一个函数,则该函数不能调用print,因为我们在代码之后删除了返回真实环境的链接回来。所以我认为解决这个问题的最好方法是让环境保持链接,然后复制函数等。
function loadAndReturnEnvWithGlobals2(codeString)
local env = {}
setmetatable(env, {__index=_ENV})
load(codeString, nil, nil, env)() -- error checking skipped for demonstration
local result = {}
for k,v in pairs(env) do result[k]=v end
return result
end
虽然这会复制值,所以如果您有任何更改变量的函数,您将不会看到更改的变量。
您使用哪个版本取决于您。它们各有优缺点。