创建模块的基本方法的缺点在于,忘记使用local,很容易就污染全局空间。
“函数环境”是一种有趣的技术,它能够解决上面的问题。就是让模块的主程序块独占一个环境。
这样不仅它的所有函数可以共享这个table,而且它的所有全局变量也都记录在这个table中。还可以将所有的公有函数声明为全局变量。
这样它们就自动地记录在一个独立的table中了。模块所要做的就是将这个table赋予模块名和package.loaded:
local M = {}
_ENV = M
function add (c1,c2)
return new(c1,r + c2.r, c1.i + c2.i)
end
当我们声明add函数后,它自动就到M.add中去了。在该模块中调用其它函数时,也不再需要前缀,new就会去找M.new。
这样在调用一个导出的函数与一个私有函数就没有任何区别可言。就算忘记写local也不会污染全局命名空间,只会将一个私有函数变成了公有而已。
但是问题来了:我们改变了_ENV环境变量,导致我们无法访问之前的全局变量。
这里有几种方法可以解决(褒贬不一):
1》继承
local M = {}
setmetatable(M,{__index = _G})
_ENV = M
现在module可以直接访问全局标识符,每次访问只需要付出很小的开销。这种方法导致了一个后果,从概念上说,此时的模块中包含了所有的全局变量。
例如,通过该模块调用正弦函数:
complex.math.sin(x)
感觉像有点越界,不受控制了。
2》用局部变量保存旧环境
local M = {}
local _G = _G
_ENV = M -- or _ENV = nil
此方法必须在所有全局变量的名称前加上"_G",由于没有涉及到元方法,这种访问会比前面的方法快些。
3》只加载需要的模块或函数(以local变量的方式)
--模块设置
local M = {} -- Import section: 导入段
-- declare everything this module needs from outside
-- 声明这个模块需要从外部引入的所有东西
local sqrt = math.sqrt
local io = io
-- no more external access after this point
--之后就不再需要外部的访问了
_ENV = nil -- or _ENV = M
这种方法比较正规,但是这种技术要求做更多的工作,但是它能清晰地说明模块的依赖性。
同时,比之前俩个方法运行的速度更快,应为它使用了local 变量。
以上内容来自:《Lua程序设计第二版》和《Programming in Lua third edition 》