全局变量声明的规范化(转)
转载
_G中,(_G._G等于_G)。为了对全局命名空间更好的管理,最好是显示的声明每一个变量,可以通过使用metamethod来改变_G的行为来进行规范:
-- 声明新的变量,使用rawset绕过metamethod的限制
-- 保证声明的变量不为nil,如果声明一个nil变量,等于该变量还是不存在,访问该变量的时候还是会触发__index
declare = function(name, initval)
rawset(_G, name, initval or false)
end
--改变全局变量的访问行为
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,}
)
declare("a")
a = 1
print(a) -- 1
a = nil
print(a) -- error 触发__index,因为该变量已经释放了
上述代码中,每一个变量都需要显示declare,并且不能声明nil变量。
下面这种方法可以声明nil变量,但需要用一个表来显示的记录每一个声明过的变量:
local declareNames = {}
function declare(name, initval)
rawset(_G, name, initval)
declareNames[name] = true
end
setmetatable(_G,
{__newindex = function (t, n, v)
if not declareNames[n] then
error("attempt to write to undeclared var. " ..n, 2)
else
rawset(t, n, v)
end
end,
__index = function (_, n)
if not declareNames[n] then
error("attempt to read undeclared var. "..n, 2)
else
return nil
end
end}
)
declare("a")
a = 1
print(a) -- 1
a = nil
print(a) -- nil 还是可以访问
个人偏向于第一种方法,因为第二种方法中,当一个变量被置为nil以后,实际上该变量已经被释放了,所以将变量的置空操作封装在一个undeclear函数中。对第二种方法的改进:
local declareNames = {}
function declare(name, initval)
rawset(_G, name, initval)
declareNames[name] = true
end
function undeclare(name)
declareNames[name] = nil
rawset(_G, name, nil)
end
setmetatable(_G,
{__newindex = function (t, n, v)
if not declareNames[n] then
error("attempt to write to undeclared var. " ..n, 2)
else
rawset(t, n, v)
end
end,
__index = function (_, n)
if not declareNames[n] then
error("attempt to read undeclared var. "..n, 2)
else
return nil
end
end}
)
declare("a")
a = 1
print(a) -- 1
undeclare("a")
print(a) -- error 变量已经不存在了,会触发__index
两种方法的代价都很小,可以忽略不计,但是给整个编程环境带来的好处是不言而喻的。