第一版是为 Lua 5.0 编写的。虽然在很大程度上仍然适用于较新版本,但有一些不同之处。
第四版针对 Lua 5.3,可在 Amazon 和其他书店购买。
购买本书,您还可以帮助 支持 Lua 项目。
![]() |
Lua 中的编程 Lua | ![]() |
| 第二部分。表和对象 第 14 章。环境 |
Lua 中的全局变量不需要声明。虽然这对于小型程序来说很方便,但在大型程序中,一个简单的错别字可能会导致难以发现的错误。但是,如果我们愿意,可以更改这种行为。因为 Lua 将其全局变量保存在一个常规表中,所以我们可以使用元表来更改其在访问全局变量时的行为。
第一种方法如下
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,
})
在该代码之后,任何尝试访问不存在的全局变量都会触发一个错误
> a = 1
stdin:1: attempt to write to undeclared variable a
但是我们如何声明新变量?使用 rawset,它绕过了元方法
function declare (name, initval)
rawset(_G, name, initval or false)
end
使用 or 和 false 确保新的全局变量始终获得与 nil 不同的值。请注意,您应该在安装访问控制之前定义此函数,否则会收到错误:毕竟,您正在尝试创建一个新的全局变量 declare。有了该函数,您可以完全控制全局变量
> a = 1
stdin:1: attempt to write to undeclared variable a
> declare"a"
> a = 1 -- OK
但是现在,要测试变量是否存在,我们不能简单地将其与 nil 进行比较;如果它是 nil,访问将引发错误。相反,我们使用 rawget,它避免了元方法
if rawget(_G, var) == nil then
-- `var' is undeclared
...
end
将该控件更改为允许具有 nil 值的全局变量并不困难。我们只需要一个辅助表来保存已声明变量的名称。每当调用元方法时,它都会在该表中检查变量是否已声明。代码可能如下所示
local declaredNames = {}
function declare (name, initval)
rawset(_G, name, initval)
declaredNames[name] = true
end
setmetatable(_G, {
__newindex = function (t, n, v)
if not declaredNames[n] then
error("attempt to write to undeclared var. "..n, 2)
else
rawset(t, n, v) -- do the actual set
end
end,
__index = function (_, n)
if not declaredNames[n] then
error("attempt to read undeclared var. "..n, 2)
else
return nil
end
end,
})
对于这两种解决方案,开销可以忽略不计。使用第一个解决方案,在正常操作期间永远不会调用元方法。在第二个解决方案中,它们可能会被调用,但仅当程序访问保存 nil 的变量时。
| 版权所有 © 2003–2004 Roberto Ierusalimschy。保留所有权利。 | ![]() |