在 Lua 中,全局变量和函数存在风险,在考虑创建/添加到 _G
表(Lua 的“全局表”)之前,您应该了解一些关键事项。
首先,让我们快速讨论一下 _G
表。在 Lua 中,所有大写字母并以下划线 (_
) 开头的变量都被视为 Lua 使用的私有变量,例如 _VERSION
。在任何情况下,您都不应尝试在程序中使用这些变量。考虑到这一点,请注意 _G
也是大写字母并以下划线开头!这意味着它是系统使用的保留结构。
全局变量和函数确实很方便。通过使用它们,您可以创建一个可以从 main.lua
和任何其他 Lua 模块(例如 Composer 场景)访问的对象。但是,重要的是要理解以下两个变量完全相同:
myVariable = 10 _G.myVariable = 10
换句话说,Lua 将每个全局变量放入 _G
表中,并且您显式放入 _G
中的所有内容都可以通过其变量名访问。例如:
_G.myVariable = 20 print( myVariable ) --> Prints 20 to the console
那么,为什么要像上面的示例那样将全局变量显式放入 _G
表中呢?一个原因是明确标识您正在声明一个全局变量,而不是一个您打算声明为局部变量但只是忘记添加 local
前缀的变量。本质上,在全局声明前加上 _G.
可以清楚地表明您在返回检查/修改代码时的意图。
一些开发者报告说,当他们使用 print()
将变量输出到控制台时,Solar2D 的错误和警告消息停止工作。可能的原因是开发者创建了一个名为 debug
的全局变量,即 _G.debug
,这样做无意中“破坏”了对 Solar2D 用于输出调试消息的关键内部库的访问。
要进一步检查此概念,您可以使用以下方法 print()
整个 _G
表:
for k,v in pairs( _G ) do print( k .. " : ", v ) end
运行此代码将输出 _G
的全部内容,包括:
_G : table: 0x610000468580 _VERSION : Lua 5.1 package : table: 0x600000870300 tostring : function: 0x60000024d290 print : function: 0x60000024c870 lpeg : table: 0x610000463180 os : table: 0x600000a6c240 unpack : function: 0x60000024cf00 lfs : table: 0x610000469e80 require : function: 0x60000067af00 debug : table: 0x600000270a80
API 参考 中(全局变量)条目下列出的所有 API,以及一些未记录的项目,如 debug
和几个核心 API 组,如 math
、string
和 audio
。考虑到这一点,您可能会认为您可以编写如下代码:
_G.audio = true -- Play audio if true, don't play if false _G.type = "Gold" _G.timer = timer.performWithDelay( 1000, doSomething )
但是,这样做只会“破坏”这些内部 Solar2D 库,这意味着您将无法播放音频或使用 type()
函数 — 并且计时器将无法计时!
您可能认为解决方案是简单地避免使用那些全局名称。别着急!上面循环输出的列表指示了您今天不应该覆盖的库,基于一个空的 main.lua
文件。该列表还假设您尚未:require()
-d
全局变量危险的另一个原因是,由于它们没有可见性限制,因此在性能方面使用成本可能很高。事实上,如果在
一些开发者使用(有时是滥用)全局变量来访问尚未声明的内容,或在模块之间访问变量/函数。幸运的是,这两项任务都可以无需使用全局变量轻松完成!
第一种情况(访问尚未声明的内容)可以通过前置声明方法解决。作为 Lua/Solar2D 程序员,了解作用域至关重要。如果您需要在声明变量(通常是函数)之前使用它,只需
local doSomething -- Forward declaration local function makeSomethingHappen() local count = 10 doSomething( count ) end doSomething = function( count ) -- Assign function to forward declaration print( count ) end makeSomethingHappen()
在第 8 行,我们只是将执行工作的函数分配给在第 1 行doSomething
。这样,makeSomethingHappen()
函数doSomething()
函数,因为它是在请求它的代码块“上方”声明的。
要了解更多关于 Lua 作用域和遵守作用域规则的重要性,请参阅 初学者作用域 教程。
要解决第二种情况(在模块之间访问变量),您可以简单地创建一个名为 globalData.lua
(或类似名称)的 Lua 文件,其内容如下:
-- Pseudo-global space local M = {} return M
然后,在您的每个 Lua 模块中,只需按如下方式 require()
该模块:
local globalData = require( "globalData" )
之后,设置“全局”变量就像这样简单:
globalData.playerName = "John Smith"
并且从任何模块读取值(假设您包含了local globalData = require( "globalData" )
)—
local currentPlayer = globalData.playerName
如您所见,此方法允许您像使用 _G
一样使用 globalData
表,并避免覆盖 _G
表中内容的风险,以及避免全局变量带来的常见问题和陷阱。
我们一直强调“避免全局变量!”的立场,正如您通过上述方法所看到的,基本上不再需要全局变量或函数。通过谨慎的作用域和使用