lua中默认申明的变量都是全局的有什么好处
1个回答
展开全部
Lua的设计里面,table是作为容器出现的,它不仅是数据,是对象。也是代码的运行环境。每个lua的function都有且仅有一个环境关联(可以参考setfenv, getfenv),你的lua文件也可以被视为一个大的匿名函数被执行,它的关联环境就是_G。
在函数中访问的变量可以分为4种,参数,函数内local,upvalue和所谓的全局变量,参数和内部local都属于函数自身不谈,如果读取的变量不在其中,Lua会查询upvalues中是否存在,存在就使用,这也是闭包的原理,如果依然不存在,Lua会查询函数所关联的环境,通常是_G,所谓全局变量也就是_G本身的字段罢了。
那么,反过来看写入值,同样,如果变量名在参数,内部local或者upvalue内,那么自然可以保存在对应的变量内,如果不在,那么自然就应该保存在关联环境的字段中。
特别的,函数的环境是可以改变的,通过setfenv(Lua 5.1)或者_ENV(Lua 5.2) 调整了函数的环境后,你可以看到所谓全局变量并不会被保存在_G中,也不会被其他环境是_G的函数读取到。
所以,全局变量在Lua中算是一个伪概念,不过对于新手来说会更容易接受。
补充: 上面只提及了全局变量的实际概念,倒是没提及它的好处。关于共享数据之类,使用闭包好还是全局好,都是视需求来说,这点也谈不上什么好处。
我就从不少人认为Lua的全局变量的坏处部分聊聊,对于大型项目来说,都是按功能分别开发的,为了避免不同模组的变量冲突,就需要要求各个模组仅申明local变量,漏申明什么的,可能造成的问题都很难debug到。(至于使用local形成闭包提高函数运行速度啥的,通常使用脚本的地方并没有追求性能到这个地步,何况大数据处理也都有其他优化的办法。)
实际如果希望每个模组不受其他的代码干扰,就应该为每个模组构建沙盒环境,比如我在其它回答中贴过的一段代码:
setfenv(1, setmetatable({}, {
__index = function(self, key)
local val = _G[key] or error("The global " .. key .. " can't be found.", 2)
rawset(self, key, val)
return val
end
}))
print(ture) -- Error: the global ture can't be found.
使用setfenv或者_ENV调整当前代码的运行环境后,就拥有了一个完整的沙盒环境,并且还自带拼写错误检查和自动缓存。(当然实际项目使用的代码会复杂的多)
使用沙盒环境后,全局变量冲突的情况已经避免了,但还谈不上好处,只能说和使用local变量持平。再进一步来看,沙盒环境能带给我们什么,全局变量的读写(还不存在时)在这个私有环境中是可控的,上面的例子读取,这个还体现不出特别的价值。但写可控可以做的文章很多,其中一种是Lua版decorator :
_ENV = Module "Test" -- 启用沙盒环境
-- 注册一个修饰器,这个修饰器将函数封装为协程调用方式
__Async__()
-- 沙盒环境获知DoTask被定义,调用注册的修饰器进行封装,
-- 返回的封装函数被保存在沙盒环境的DoTask字段中
function DoTask()
for i = 1, 10 do
print(i)
-- 伪Sleep,停止当前协程,等待1秒继续
Task.Sleep(1)
end
end
-- 调用DoTask
DoTask()
通过这种技术,我们可以实现很多的语言特性,例如参数验证,重载,debug(记录调用堆栈信息)等。这些都是依赖伪全局变量这个概念才能实现。当然这个话题扯得有点远了,不是大型项目也没必要做到这个地步。
在函数中访问的变量可以分为4种,参数,函数内local,upvalue和所谓的全局变量,参数和内部local都属于函数自身不谈,如果读取的变量不在其中,Lua会查询upvalues中是否存在,存在就使用,这也是闭包的原理,如果依然不存在,Lua会查询函数所关联的环境,通常是_G,所谓全局变量也就是_G本身的字段罢了。
那么,反过来看写入值,同样,如果变量名在参数,内部local或者upvalue内,那么自然可以保存在对应的变量内,如果不在,那么自然就应该保存在关联环境的字段中。
特别的,函数的环境是可以改变的,通过setfenv(Lua 5.1)或者_ENV(Lua 5.2) 调整了函数的环境后,你可以看到所谓全局变量并不会被保存在_G中,也不会被其他环境是_G的函数读取到。
所以,全局变量在Lua中算是一个伪概念,不过对于新手来说会更容易接受。
补充: 上面只提及了全局变量的实际概念,倒是没提及它的好处。关于共享数据之类,使用闭包好还是全局好,都是视需求来说,这点也谈不上什么好处。
我就从不少人认为Lua的全局变量的坏处部分聊聊,对于大型项目来说,都是按功能分别开发的,为了避免不同模组的变量冲突,就需要要求各个模组仅申明local变量,漏申明什么的,可能造成的问题都很难debug到。(至于使用local形成闭包提高函数运行速度啥的,通常使用脚本的地方并没有追求性能到这个地步,何况大数据处理也都有其他优化的办法。)
实际如果希望每个模组不受其他的代码干扰,就应该为每个模组构建沙盒环境,比如我在其它回答中贴过的一段代码:
setfenv(1, setmetatable({}, {
__index = function(self, key)
local val = _G[key] or error("The global " .. key .. " can't be found.", 2)
rawset(self, key, val)
return val
end
}))
print(ture) -- Error: the global ture can't be found.
使用setfenv或者_ENV调整当前代码的运行环境后,就拥有了一个完整的沙盒环境,并且还自带拼写错误检查和自动缓存。(当然实际项目使用的代码会复杂的多)
使用沙盒环境后,全局变量冲突的情况已经避免了,但还谈不上好处,只能说和使用local变量持平。再进一步来看,沙盒环境能带给我们什么,全局变量的读写(还不存在时)在这个私有环境中是可控的,上面的例子读取,这个还体现不出特别的价值。但写可控可以做的文章很多,其中一种是Lua版decorator :
_ENV = Module "Test" -- 启用沙盒环境
-- 注册一个修饰器,这个修饰器将函数封装为协程调用方式
__Async__()
-- 沙盒环境获知DoTask被定义,调用注册的修饰器进行封装,
-- 返回的封装函数被保存在沙盒环境的DoTask字段中
function DoTask()
for i = 1, 10 do
print(i)
-- 伪Sleep,停止当前协程,等待1秒继续
Task.Sleep(1)
end
end
-- 调用DoTask
DoTask()
通过这种技术,我们可以实现很多的语言特性,例如参数验证,重载,debug(记录调用堆栈信息)等。这些都是依赖伪全局变量这个概念才能实现。当然这个话题扯得有点远了,不是大型项目也没必要做到这个地步。
本回答被网友采纳
已赞过
已踩过<
评论
收起
你对这个回答的评价是?
推荐律师服务:
若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询