Chapter 15_3 使用环境

时间:2022-01-11 06:48:49

  创建模块的基本方法的缺点在于,忘记使用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 》