很快就要开始介绍Lua里的“面向对象”了,在此之前,我们先来了解一下Lua的模块。

1.编写一个简单的模块

Lua的模块是什么东西呢?通常我们可以理解为是一个table,这个table里有一些变量、一些函数…

等等,这不就是我们所熟悉的类吗?

没错,和类很像(实际上我说不出它们的区别)。
 
我们来看看一个简单的模块,新建一个文件,命名为game.lua,代码如下:

复制代码代码如下:

game = {}
function game.play()
    print("那么,开始吧");
end
function game.quit()
    print("你走吧,我保证你不会出事的,呵,呵呵");
end
return game;

我们定义了一个table,并且给这个table加了两个字段,只不过这两个字段的值是函数而已。

至于如何使用模块,那就要用到我们之前介绍过的require了。
 
我们在main函数里这么使用:

复制代码代码如下:

local function main()
    cc.FileUtils:getInstance():addSearchPath("src")
    game = require("game");
   
    game.play();
end

注意,我们要require其他文件的时候,要把文件路径给设置好,否则会找不到文件。
因为我使用的是Cocos Code IDE,直接调用addSearchPath函数就可以了,我的game.lua文件是在src目录下的。
 
好了,运行代码,结果如下:

复制代码代码如下:

[LUA-print] 那么,开始吧

OK,这就是一个很简单的模块,如果我们习惯了Java、C++等面向对象语言,那也可以简单地把模块理解为类。

2.为以后的自己偷懒——避免修改每个函数中的模块名

假设我们想把刚刚的game模块改个名字,改成eatDaddyGame,那么,我们需要做以下两件事情:

1).修改game.lua的文件名
2).修改game.lua的内容,把所有的game改成eatDaddyGame
 
目前的game.lua函数还算少,就两个,实际上一个模块的函数肯定不会少的,那么,要这么去改这些函数,太烦了。

如果批量修改,又怕有哪个地方改错。

于是,我们可以这么偷懒:

复制代码代码如下:

game = {}
local M = game;
function M.play()
    print("那么,开始吧");
end
function M.quit()
    print("你走吧,我保证你不会出事的,呵,呵呵");
end
return M;

我们用一个局部变量M来代替了game,于是,以后我们只需要修改前面两个的game就可以了,函数部分的内容完全不需要去修改。

这个偷懒其实蛮有用的,某些情况下,修改越少,越安全~

3.更进一步的偷懒——模块名参数

实际上,我们可以更加得偷懒,以后修改模块名,只需要修改模块的文件名就可以了,文件内容可以不管,具体怎么实现?

看代码:

复制代码代码如下:

local M = {};
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 M = {};
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 M = {};
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 M = {};
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 M = {};
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 M = {};
local modelName = ...;
_G[modelName] = M;
setmetatable(M, {__index = _G});
setfenv(1, M);

就这几句代码,其实我们可以忽略不写,因为,我们有module函数,它的功能就相当于写了这些代码。
我们修改一下game.lua的内容,如下代码:

复制代码代码如下:

module(..., package.seeall);
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 文件中

复制代码代码如下:

print(“hello world!”)

当我require(“my”)时,那么会直接输出hello world!

特别注意:

1、用require载入相同的文件时,只有第一次执行,以后都不执行。

2、如果你想让每次载入都执行文件,那么可以使用dofile(“my.lua”)

3、如果你想载入的时候不执行文件,等需要的时候再执行文件,那么可以使用loadfile(“my.lua”)

复制代码代码如下:

local my=loadfile(“my.lua”)

...

my()

Lua中的模块与module函数详解相关推荐

  1. python中argparse模块关于 parse_args() 函数详解(全)

    目录 前言 1. 函数讲解 2. 基本用法 3. 实战讲解 前言 原理:命令行解析使用argparse包 作用:命令行传参赋值 可用在机器学习深度学习 或者 脚本运行等 了解这个函数需要了解其背后的原 ...

  2. 【转】angularjs指令中的compile与link函数详解

    这篇文章主要介绍了angularjs指令中的compile与link函数详解,本文同时诉大家complie,pre-link,post-link的用法与区别等内容,需要的朋友可以参考下 通常大家在使用 ...

  3. python中transpose函数_对numpy中的transpose和swapaxes函数详解

    transpose() 这个函数如果括号内不带参数,就相当于转置,和.T效果一样,而今天主要来讲解其带参数. 我们看如下一个numpy的数组: `arr=np.arange(16).reshape(( ...

  4. python中zip的使用_浅谈Python中的zip()与*zip()函数详解

    前言 1.实验环境: Python 3.6: 2.示例代码地址:下载示例: 3.本文中元素是指列表.元组.字典等集合类数据类型中的下一级项目(可能是单个元素或嵌套列表). zip(*iterables ...

  5. java里的进制转换函数_基于Java中进制的转换函数详解

    十进制转成十六进制: Integer.toHexString(int i) 十进制转成八进制 Integer.toOctalString(int i) 十进制转成二进制 Integer.toBinar ...

  6. node怎么解析vue代码_vue中node_modules中第三方模块的修改使用详解

    最近用vue在做一个项目,github用上找了一个日历插件,intall到了本项目中,配好以后发现插件的样式风格和项目总体风格相差较大,所以就像这个改一下插件风格, 第一种方法:我直接在父组件中将st ...

  7. python中yaml模块的使用_详解Python yaml模块

    一.yaml文件介绍 yaml是一个专门用来写配置文件的语言. 1. yaml文件规则 区分大小写: 使用缩进表示层级关系: 使用空格键缩进,而非Tab键缩进 缩进的空格数目不固定,只需要相同层级的元 ...

  8. angularjs指令中的compile与link函数详解

    通常大家在使用ng中的指令的时候,用的链接函数最多的是link属性,下面这篇文章将告诉大家complie,pre-link,post-link的用法与区别. angularjs里的指令非常神奇,允许你 ...

  9. pythoncopy函数_Python的shutil模块中文件的复制操作函数详解

    copy()chutil.copy(source, destination) shutil.copy() 函数实现文件复制功能,将 source 文件复制到 destination 文件夹中,两个参数 ...

最新文章

  1. Spring junit测试
  2. python查询最高分_精通 Oracle+Python,第 1 部分:查询最佳应践
  3. 一起谈.NET技术,Silverlight 游戏开发小技巧:实现街霸4的选人界面
  4. Bootstrap3基础 glyphicon 设置图标的颜色与大小
  5. 阿里云CDN缓存总结
  6. php urledcode_php慎用urldecode函数
  7. 题目:学习成绩 = 90分的同学用A表示,60 - 89分之间的用B表示,60分以下的用C表示
  8. IDEA java 显示build目录
  9. uniapp某个页面高度占满写法
  10. linux打开mid格式音乐,mid文件扩展名,mid文件怎么打开?
  11. 30 个数据工程必备的Python 包
  12. 旧主机改NAS最终成功的过程
  13. 不平衡数据对于卷积神经网络的影响
  14. 青鸟s1java云题库答案_北大青鸟S1java内部测试试卷(试题)
  15. 国内和国外DNS服务器地址 全国各地电信DNS服务器地址
  16. mysql 8.0 直方图_MySQL 8.0 中统计信息直方图的尝试
  17. 大数据时代,给我们带来了哪些改变
  18. No module named 'exceptions'
  19. 《算法导论》CLRS算法C++实现(十一)P163 红黑树
  20. 人工只能对php的影响,阿西莫夫眼中的人工智能

热门文章

  1. rhel6硬盘或U盘安装注意的问题
  2. axios 参数对象的值为数组时,后端无法接收(待解决)
  3. ospf和pat及nat的配置
  4. EventBus简单分析
  5. 多台linux无密码访问之方法二
  6. magento获取判断当前页或句柄handles
  7. 【译】Asp.net MVC 利用自定义RouteHandler来防止图片盗链 (转)
  8. 打破认知:程序设计 #x3D; 算法 + 数据结构?
  9. easypoi实现Excel导入
  10. win 对比工具-Araxis Merge