学习Cocos2d-x Lua:全局变量与非全局环境

2015年03月25日 11:43 0 点赞 0 评论 更新于 2017-05-09 19:45

在这个系列中,我们将深入学习Cocos2d-x Lua,总结Lua开发过程中涉及的知识点,并探讨如何在开发过程中使用Cocos Code IDE。今天,我们聚焦于两个重要话题:全局变量和非全局环境。全局变量的概念相对简单,而非全局环境则需要我们更深入地思考。

1. 全局变量的本质

在Lua里,声明全局变量非常简单,只需在定义变量时不添加local关键字即可。实际上,全局变量本质上是一个table,它将我们创建的所有全局变量都存储在其中。这个特殊的table名为_G

以下是相关代码示例:

-- 定义一个全局变量
gName = "哎哟,很挫哦";
-- 用三种方式输出变量的值
print(gName);
print(_G["gName"]);
print(_G.gName);

输出结果如下:

[LUA-print]哎哟,很挫哦
[LUA-print]哎哟,很挫哦
[LUA-print]哎哟,很挫哦

我们定义了全局变量gName,它成为了_G的一个字段。可以看到,全局变量的使用十分直观。

2. 非全局环境的引入

在各种编程语言中,大家常常会被提醒:“不要滥用全局变量,后果自负”。或许正是出于这个原因,Lua引入了一种特殊机制——非全局环境。我将其形容为“不会造成全局影响的全局变量”。

3. 改变函数的全局变量环境——setfenv函数

首先,让我们看一段代码:

-- 定义一个全局变量
gName = "哎哟,很挫哦";
-- 将当前全局环境重新设置为新的table
setfenv(1, {});
-- 输出值
print(gName);

如果运行这段代码,输出结果会是:

[LUA-print]LUA ERROR: [string "src/main.lua"]:107: attempt to call global ‘print’ (a nil value)

为什么会出现这种情况,甚至连print函数都找不到了呢?这是因为我们使用setfenv函数改变了当前函数范围内的全局变量环境。通常情况下,全局变量默认存储在_G中,但现在我们将其设置为一个新的空table,这个新table中没有任何全局变量,所以print函数也无法被找到。

setfenv函数用于改变某个函数范围里的全局环境,简单来说,就是替换了该函数范围内的_G。该函数有两个参数:

  • 第一个参数可以是即将改变环境的函数,也可以是一个数字。数字1代表当前函数,数字2代表调用当前函数的函数,依此类推。
  • 第二个参数是新的全局环境table

4. 保留原来的_G

由于改变全局环境后连print函数都无法使用,这给测试带来了不便。我们可以采取一个小技巧来保留原来的_G。以下是示例代码:

-- 定义一个全局变量
gName = "哎哟,很挫哦";
-- 将当前全局环境重新设置为新的table
setfenv(1, {g = _G});
-- 输出值
g.print(gName);
-- 再次定义一个全局变量
gName = "哎哟,有点错哦";
-- 再次输出值
g.print(gName);
-- 输出原来的值
g.print(g.gName);

输出结果如下:

[LUA-print]nil
[LUA-print]哎哟,有点错哦
[LUA-print]哎哟,很挫哦

下面对三次调用g.print函数的输出结果进行详细分析:

  • 第一次调用时,我们刚刚重新设置了全局环境,此时当前函数的全局变量只有g,所以gName的值为nil
  • 第二次调用前,我们重新对gName进行了赋值,由于是在新环境中操作,所以此时gName有了新的值。
  • 第三次调用时,我们通过g.gName访问的是原先全局环境里的gName值,因此仍然是最初的“哎哟,很挫哦”。

从这个例子中,我们可能暂时看不出这种做法的特别之处。不过,随着学习的深入,我们会逐渐发现其更多的用途。

5. 使用__index元方法保留原来的_G

前面我们介绍了保留_G的方法,但调用print等函数时需要使用g.print的形式,略显繁琐。我们可以利用__index元方法来解决这个问题。以下是示例代码:

-- 定义一个全局变量
gName = "哎哟,很挫哦";
-- 一个table,即将成为新的环境
local newG = {};
setmetatable(newG, {__index = _G});
-- 将当前全局环境重新设置为新的table
setfenv(1, newG);
gName = "别再哎哟了,很烦!";
-- 输出值
print(gName);
print(_G.gName);

我们为新的table设置了一个元表,该元表的__index元方法指向_G。这样,当在新环境中找不到print等字段时,Lua会自动去_G中寻找。

输出结果如下:

[LUA-print]别再哎哟了,很烦!
[LUA-print]哎哟,很挫哦

第一次输出的是新环境里的gName值,第二次输出的是原来环境里的gName值,二者互不影响。

6. 总结

关于全局变量和非全局环境,我们就先介绍到这里。虽然目前可能感觉这些内容的作用不太明显,但它们是后续深入学习的基础。随着学习的推进,我们会不断发现它们的更多用途,我也会继续和大家分享相关内容。

作者信息

boke

boke

共发布了 1025 篇文章