第一版是为 Lua 5.0 编写的。虽然对于后续版本仍然有很大关联,但存在一些差异。
第四版针对 Lua 5.3,可在 Amazon 和其他书店购买。
购买本书,您还将帮助 支持 Lua 项目


14.3 – 非全局环境

环境的一个问题是它是全局的。您对它进行的任何修改都会影响程序的所有部分。例如,当您安装元表来控制全局访问时,整个程序都必须遵循这些准则。如果您想使用未声明全局变量的库,那么您很倒霉。

Lua 5.0 通过允许每个函数拥有自己的环境来改善此问题。这听起来可能很奇怪;毕竟,全局变量表的目的是全局的。但是,在 第 15.4 节 中,我们将看到此功能允许多种有趣的构造,其中全局值仍然可以在任何地方使用。

您可以使用 setfenv 函数(设置函数环境)来更改函数的环境。它接收函数和新环境。除了函数本身,您还可以指定一个数字,表示给定堆栈级别处的活动函数。数字 1 表示当前函数,数字 2 表示调用当前函数的函数(这对于编写辅助函数非常方便,这些函数可以更改其调用者的环境),依此类推。

首次尝试使用 setfenv 会惨遭失败。代码

    a = 1   -- create a global variable
    -- change current environment to a new empty table
    setfenv(1, {})
    print(a)
导致
    stdin:5: attempt to call global `print' (a nil value)
(您必须在单个块中运行该代码。如果您在交互模式下逐行输入,则每一行都是一个不同的函数,并且对 setfenv 的调用只会影响其自己的行。)一旦您更改了环境,所有全局访问都将使用此新表。如果它是空的,您将丢失所有全局变量,甚至 _G。因此,您应该首先使用一些有用的值(例如旧环境)填充它
    a = 1   -- create a global variable
    -- change current environment
    setfenv(1, {_G = _G})
    _G.print(a)      --> nil
    _G.print(_G.a)   --> 1
现在,当您访问“全局”_G 时,它的值是旧环境,在其中您将找到字段 print

您还可以使用继承来填充新环境

    a = 1
    local newgt = {}        -- create new environment
    setmetatable(newgt, {__index = _G})
    setfenv(1, newgt)    -- set it
    print(a)          --> 1
在此代码中,新环境同时从旧环境继承了 printa。不过,任何赋值都转到新表。虽然仍可以通过 _G 更改它们,但不会错误地更改真正全局变量。
    -- continuing previous code
    a = 10
    print(a)      --> 10
    print(_G.a)   --> 1
    _G.a = 20
    print(_G.a)   --> 20

创建新函数时,它从创建它的函数继承其环境。因此,如果一个块更改其自身环境,它随后定义的所有函数都将共享此相同环境。这是一个创建命名空间的有用机制,我们将在下一章中看到。