目录

  1. 具有动态名称的全局变量
  2. 非全局环境
  3. 环境和模块
  4. _ENV和load

只有疯狂过,你才知道自己究竟能不能成功。

具有动态名称的全局变量

在lua中,所有的全局变量都被存在_G中,通过_G[name]可以访问到任意一个全局变量。在lua中,全局变量不需要声明就可以直接使用,但是这个可能会造成非常难以查询的bug,所以我们可以对全局变量进行简单的封装。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
setmetatable(_G, {
    __newindex = function(_, n)
        error("attempt to write to undeclared variable" .. n, 2)
    end,
    __index = function(_, n)
        error("attempt to read undeclared variable" .. n, 2)
    end
})
--这样我们怎么对全局变量初始化呢?使用rawset方法。
function declare(name, initval)
    rawset(_G, name, initval or false)
end

–改版后的
local declaredNames = {}
setmetatable(G, {
__newindex = function(t, n, v)
if not declaredNames[n] then
local w = debug.getinfo(2, "S").what
if w ~= "main" and w ~= "C" then
error("attempt to write to undeclared variable" n, 2)
end
declaredNames[n] = true
end
rawset(t, n, v)
end,
__index = function(
, n)
if not declaredNames[n] then
error("attempt to read undeclared variable" n, 2)
else
return nil
end
end
})

非全局环境

lua语言中处理全局变量的方式:

  • 编译器在编译所有代码之前,在外层创建局部变量_ENV
  • 编译器将所有自由名称变换为_ENV.var;
  • 函数load(or loadfile)使用全局环境初始化代码段的第一个上值,即lua语言内部维护的一个普通表。

_ENV只是一个普通的变量,将其赋值为nil会使得后续的代码不能直接访问全局变量。

我们还可以使用_ENV来绕过局部声明的变量,直接访问全局变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
local print, sin = print, math.sin
_ENV = nil
print(13)
print(sin(13))
print(cos(13)) --error 访问不到全局

a = 13
local a = 12
print(a)
print(_ENV.a) –访问全局的a,当然也可以使用_G来访问全局的a

_ENV = {}
a = 1
print(a) –print is a nil

a = 15
_ENV = {g = _G}
a = 1
g.print(_ENV.a, g.a} – 1 , 15

通常_G和_ENV指向的是同一个表。但是,尽管如此,他们是很不一样的实体。_ENV是一个局部变量,所以对“全局变量”的访问实际上访问的都是_ENV。_G则是一个在任何情况下都没有任何特殊状态的全局变量。_ENV永远指向的是当前的环境;而假设在可见且无人改变过其值的前提下,_G通常指向的是全局变量。

_ENV的主要作用就是改变当前的环境。

环境和模块

为了防止污染全局环境:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
local M = {}
_ENV = {}

function hello()
print("hello")
end

function sayHello()
hello() –M.hello
end

local M = {}
local sqrt = math.sqrt
local in = io
_ENV = nil
–这样就不能进行外部访问了

load函数通常被加载代码段的上值_ENV初始化为全局变量。

_ENV和load

函数load通常把加载代码段的上值_ENV初始化为全局变量。不过,函数load还有一个可选的第四个参数来让我们为_ENV指定一个不同的初始值。

1
2
3
4
5
6
7
--file "config.lua"
width = 200
height = 100

–加载文件
env = {}
loadfile("config","t",env)()

配置文件中的代码会运行在空的环境env中,类似于某种沙盒。特别的,所有的定义都会进入这个环境中。即使出错,配置文件也无法影响任何别的东西。

            <hr style="visibility: hidden;"/>
            
            <hr style="visibility: hidden;"/>