Lua中的模块与module函数详解
很快就要开始介绍Lua里的“面向对象”了,在此之前,我们先来了解一下Lua的模块。
1.编写一个简单的模块
Lua的模块是什么东西呢?通常我们可以理解为是一个table,这个table里有一些变量、一些函数…
等等,这不就是我们所熟悉的类吗?
没错,和类很像(实际上我说不出它们的区别)。
我们来看看一个简单的模块,新建一个文件,命名为game.lua,代码如下:
function game.play()
print("那么,开始吧");
end
function game.quit()
print("你走吧,我保证你不会出事的,呵,呵呵");
end
return game;
我们定义了一个table,并且给这个table加了两个字段,只不过这两个字段的值是函数而已。
至于如何使用模块,那就要用到我们之前介绍过的require了。
我们在main函数里这么使用:
cc.FileUtils:getInstance():addSearchPath("src")
game = require("game");
game.play();
end
注意,我们要require其他文件的时候,要把文件路径给设置好,否则会找不到文件。
因为我使用的是Cocos Code IDE,直接调用addSearchPath函数就可以了,我的game.lua文件是在src目录下的。
好了,运行代码,结果如下:
OK,这就是一个很简单的模块,如果我们习惯了Java、C++等面向对象语言,那也可以简单地把模块理解为类。
2.为以后的自己偷懒——避免修改每个函数中的模块名
假设我们想把刚刚的game模块改个名字,改成eatDaddyGame,那么,我们需要做以下两件事情:
1).修改game.lua的文件名
2).修改game.lua的内容,把所有的game改成eatDaddyGame
目前的game.lua函数还算少,就两个,实际上一个模块的函数肯定不会少的,那么,要这么去改这些函数,太烦了。
如果批量修改,又怕有哪个地方改错。
于是,我们可以这么偷懒:
local M = game;
function M.play()
print("那么,开始吧");
end
function M.quit()
print("你走吧,我保证你不会出事的,呵,呵呵");
end
return M;
我们用一个局部变量M来代替了game,于是,以后我们只需要修改前面两个的game就可以了,函数部分的内容完全不需要去修改。
这个偷懒其实蛮有用的,某些情况下,修改越少,越安全~
3.更进一步的偷懒——模块名参数
实际上,我们可以更加得偷懒,以后修改模块名,只需要修改模块的文件名就可以了,文件内容可以不管,具体怎么实现?
看代码:
local modelName = ...;
_G[modelName] = M;
function M.play()
print("那么,开始吧");
end
function M.quit()
print("你走吧,我保证你不会出事的,呵,呵呵");
end
return M;
留意一下,这里有一个 local modelName = …
“…”就是传递给模块的模块名,在这里其实就是“game”这个字符串。
接着,有点微妙了,还记得之前介绍的全局环境_G吗?我们以”game”作为字段名,添加到_G这个table里。
于是,当我们直接调用game的时候,其实就是在调用_G["game"]的内容了,而这个内容就是这里的M。
能逻辑过来吗?就是这么简单,在你没有忘记_G的前提下~
4.利用非全局环境制作更简洁和安全的模块
如果说,刚刚已经达到了我们作为高(ai)智(zhe)商(teng)人群的巅峰,那,你就太天真了。
巅峰就是要拿来超越的,还记得我们的非全局环境吗?就是那个setfenv函数。
我们来看看下面的代码:
local modelName = ...;
_G[modelName] = M;
setfenv(1, M);
function play()
print("那么,开始吧");
end
function quit()
print("你走吧,我保证你不会出事的,呵,呵呵");
end
return M;
我们把game.lua这个模块里的全局环境设置为M,于是,我们直接定义函数的时候,不需要再带M前缀。
因为此时的全局环境就是M,不带前缀去定义变量,就是全局变量,这时的全局变量是保存在M里。
所以,实际上,play和quit函数仍然是在M这个table里。
于是,我们连前缀都不用写了,这真是懒到了一个极致,简直就是艺术~
另外,由于当前的全局环境是M,所以, 在这里不需要担心重新定义了已存在的函数名,因为外部的全局变量与这里无关了。
当然,如果大家现在就运行代码,肯定会报错了。
因为我们的全局环境改变了,所以print函数也找不到了。
为了解决这个问题,我们看看第5条内容吧~
5.解决原全局变量的无法找到的问题——方案1
第一个方法,就是我们之前介绍过的,使用继承,如下代码:
local modelName = ...;
_G[modelName] = M;
-- 方法1:使用继承
setmetatable(M, {__index = _G});
setfenv(1, M);
function play()
print("那么,开始吧");
end
function quit()
print("你走吧,我保证你不会出事的,呵,呵呵");
end
return M;
没错,使用__index元方法就能解决这个问题了,当找不到print等函数时,就会去原来的_G里查找。
6.解决原全局变量的无法找到的问题——方案2
第二个方法更简单,使用一个局部变量把原来的_G保存起来,如下代码:
local modelName = ...;
_G[modelName] = M;
-- 方法2:使用局部变量保存_G
local _G = _G;
setfenv(1, M);
function play()
_G.print("那么,开始吧");
end
function quit()
_G.print("你走吧,我保证你不会出事的,呵,呵呵");
end
return M;
这种方法的缺点比较明显,那就是,每次调用print等函数时,都要使用_G前缀。
7.解决原全局变量的无法找到的问题——方案3
第三个方法比较繁琐,使用局部变量把需要用到的其他模块保存起来,如下代码:
local modelName = ...;
_G[modelName] = M;
-- 方法3:保存需要使用到的模块
local print = print;
setfenv(1, M);
function play()
print("那么,开始吧");
end
function quit()
print("你走吧,我保证你不会出事的,呵,呵呵");
end
return M;
这种方法的缺点更明显了,所有用到的模块都要用局部变量声明一次,烦人。
但,就速度而言,第三种方案比第二种方案快,第二种方法又比第一种快。
但至于快多少,我也不知道,只是理论上~我也没测试。
8.你就笑吧,但,我还想更加偷懒——module函数
本以为刚刚介绍的那些技巧已经够偷懒的吧?
但Lua似乎知道我们有多懒似的,它竟然把我们把这一切都自动完成了。
再来回忆我们刚刚为了偷懒而写的几句代码:
local modelName = ...;
_G[modelName] = M;
setmetatable(M, {__index = _G});
setfenv(1, M);
就这几句代码,其实我们可以忽略不写,因为,我们有module函数,它的功能就相当于写了这些代码。
我们修改一下game.lua的内容,如下代码:
function play()
print("那么,开始吧");
end
function quit()
print("你走吧,我保证你不会出事的,呵,呵呵");
end
注意,前面的几行代码都没了,只留下了一个module函数的调用。
module函数的调用已经相当于之前的那些代码了。
而package.seeall参数的作用就是让原来的_G依然生效,相当于调用了:setmetatable(M, {__index = _G});
再次留意一下,代码末尾的return M也不见了,因为module函数的存在,已经不需要我们主动去返回这个模块的table了。
在lua中,我们可以直接使用requeire(“model_name”)来载入别的文件,文件的后缀名是.lua,载入的时候直接执行那个文件了。
比如:my.lua 文件中
当我require(“my”)时,那么会直接输出hello world!
特别注意:
1、用require载入相同的文件时,只有第一次执行,以后都不执行。
2、如果你想让每次载入都执行文件,那么可以使用dofile(“my.lua”)
3、如果你想载入的时候不执行文件,等需要的时候再执行文件,那么可以使用loadfile(“my.lua”)
...
my()
Lua中的模块与module函数详解的更多相关文章
- 【转】angularjs指令中的compile与link函数详解
这篇文章主要介绍了angularjs指令中的compile与link函数详解,本文同时诉大家complie,pre-link,post-link的用法与区别等内容,需要的朋友可以参考下 通常大家在 ...
- angularjs指令中的compile与link函数详解
这篇文章主要介绍了angularjs指令中的compile与link函数详解,本文同时诉大家complie,pre-link,post-link的用法与区别等内容,需要的朋友可以参考下 通常大家在 ...
- Pythonh中的zip()与*zip()函数详解
前言 实验环境: Python 3.6: 示例代码地址:下载示例: 本文中元素是指列表.元组.字典等集合类数据类型中的下一级项目(可能是单个元素或嵌套列表). zip(*iterables)函数详解 ...
- Python中的zip()与*zip()函数详解
前言 实验环境: Python 3.6: 示例代码地址:下载示例: 本文中元素是指列表.元组.字典等集合类数据类型中的下一级项目(可能是单个元素或嵌套列表). zip(*iterables)函数详解 ...
- angularjs指令中的compile与link函数详解(转)
http://www.jb51.net/article/58229.htm 通常大家在使用ng中的指令的时候,用的链接函数最多的是link属性,下面这篇文章将告诉大家complie,pre-link, ...
- Lua中强大的元方法__index详解
今天要来介绍比较好玩的内容:__index元方法 我是备胎,记得回头看看 咳咳,相信每一位女生都拥有或者不知不觉中拥有了一些备胎,啊!当然,又或许是成为过别人的备胎. 没有备胎的人,就不是完整的人生. ...
- angularjs指令中的compile与link函数详解补充
通常大家在使用ng中的指令的时候,用的链接函数最多的是link属性,下面这篇文章将告诉大家complie,pre-link,post-link的用法与区别. angularjs里的指令非常神奇,允许你 ...
- jQuery 源码解析(八) 异步队列模块 Callbacks 回调函数详解
异步队列用于实现异步任务和回调函数的解耦,为ajax模块.队列模块.ready事件提供基础功能,包含三个部分:Query.Callbacks(flags).jQuery.Deferred(funct) ...
- php中几个字符串替换函数详解
在php中字符替换函数有几个如有:str_replace.substr_replace.preg_replace.preg_split.str_split等函数,下面我来给大家总结介绍介绍. 一.st ...
随机推荐
- 在string资源中添加变量
public void checkModeByAction() { if(TxrjConstant.ACTION_ADD_RECIPIENT.equals(getIntent().getAct ...
- Dubbo在开发中的一些常用配置
介绍Dubbo在开发中的一些常用配置,文中内容主要参考dubbo文档配置和示例两节,详细可移步访问 传送站 1. 属性配置方法及加载顺序 属性常用配置方法主要有三种: 第一种是通过启动时在虚拟机参数 ...
- java日期格式(年月日时分秒毫秒)
package test.remote.tools.combine; import java.text.SimpleDateFormat; import java.util.Calendar; imp ...
- c++ 多个线程读一个线程写同步
这种情况一般多个线程读是不需要加锁的.就在写的时候需要加锁. 那么要做的就是让不写的时候,读不受同步限制.让多线程自由的读. 这个时候就要用读写锁 boost已经有读写锁,而c++ 14才有读写锁. ...
- Node单线程高并发原理
一.node是如何处理web请求的 浏览器中的js是单线程的,node也是单线程的.这个单线程相当于一个大管家,一切大小事务都要经过他的手才能办成,它总是把IO任务放入到任务池中. 虽然说是单线程,但 ...
- 实体格式化转xml
In the past, I've done the following to control datetime serialization: Ignore the DateTime property ...
- Using Repository Pattern in Entity Framework
One of the most common pattern is followed in the world of Entity Framework is “Repository Pattern”. ...
- 还没被玩坏的robobrowser(7)——表单操作
背景 有一些站点是需要登录之后才能抓取内容的,另外做web测试的时候登录是家常便饭. 这一节里我们就以登陆testerhome为例,讲解一下robobrowser中form的操作. 预备知识 get_ ...
- 一个酷绚的linux 桌面程序 GLX-DOCK (cario-dock)
记录一个酷绚的linux 桌面程序 GLX-DOCK (cario-dock),支持多种风格的桌面主题. http://glx-dock.org/ 优势: 多个workspaces 方便自由切换 ...
- CentOS扩展库配置
背景:经常用到第三方的库,通过yum命令查询不到.例如:yum search ilbcWarning: No matches found for: ilbc 不要感叹,CentOS没你想象的支持力度那 ...