前言编程
从Lua5.1版本开始,就对模块和包添加了新的支持,可以使用require和module来定义和使用模块和包。require用于使用模块,module用于建立模块。简单的说,一个模块就是一个程序库,能够经过require来加载。而后便获得了一个全局变量,表示一个table。这个table就像是一个命名空间,其内容就是模块中导出的全部东西,好比函数和常量,一个符合规范的模块还应使require返回这个table。windows
require函数数组
Lua提供了一个名为require的函数用来加载模块。要加载一个模块,只须要简单地调用require “<模块名>”就能够了。这个调用会返回一个由模块函数组成的table,而且还会定义一个包含该table的全局变量。可是,这些行为都是由模块完成的,而非require。因此,有些模块会选择返回其它值,或者具备其它的效果。那么require究竟是如何加载模块的呢?函数
首先,要加载一个模块,就必须的知道这个模块在哪里。知道了这个模块在哪里之后,才能进行正确的加载。当咱们写下require “mod”这样的代码之后,Lua是如何找这个mod的呢?ui
在搜索一个文件时,在windows上,不少都是根据windows的环境变量path来搜索,而require所使用的路径与传统的路径不一样,require采用的路径是一连串的模式,其中每项都是一种将模块名转换为文件名的方式。require会用模块名来替换每一个“?”,而后根据替换的结果来检查是否存在这样一个文件,若是不存在,就会尝试下一项。路径中的每一项都是以分号隔开,好比路径为如下字符串:
代码以下:lua
?;?.lua;c:\windows\?;/usr/local/lua/?/?.lua
那么,当咱们require “mod”时,就会尝试着打开如下文件:spa
mod mod.lua c:\windows\mod /usr/local/lua/mod/mod.lua
能够看到,require函数只处理了分号和问好,其它的都是由路径本身定义的。在实际编程中,require用于搜索的Lua文件的路径存放在变量package.path中,在个人电脑上,print(package.path)会输出如下内容:code
;.\?.lua;C:\Program Files (x86)\Lua\5.1\lua\?.lua;C:\Program Files (x86)\Lua\5.1\lua\?\init.lua;C:\Program Files (x86)\Lua\5.1\?.lua;C:\Program Files (x86)\Lua\5.1\?\init.lua;C:\Program Files (x86)\Lua\5.1\lua\?.luac;.\?.lua;C:\Program Files (x86)\Lua\5.1\lua\?.lua;C:\Program Files (x86)\Lua\5.1\lua\?\init.lua;C:\Program Files (x86)\Lua\5.1\?.lua;C:\Program Files (x86)\Lua\5.1\?\init.lua;;D:\Game\5.1\lua\?.luac
若是require没法找到与模块名相符的Lua文件,那Lua就会开始找C程序库;这个的搜索地址为package.cpath对应的地址,在个人电脑上,print(package.cpath)会输出如下值:字符串
.\?.dll;.\?51.dll;C:\Program Files (x86)\Lua\5.1\?.dll;C:\Program Files (x86)\Lua\5.1\?51.dll;C:\Program Files (x86)\Lua\5.1\clibs\?.dll;C:\Program Files (x86)\Lua\5.1\clibs\?51.dll;C:\Program Files (x86)\Lua\5.1\loadall.dll;C:\Program Files (x86)\Lua\5.1\clibs\loadall.dll
当找到了这个文件之后,若是这个文件是一个Lua文件,它就经过loadfile来加载该文件;若是找到的是一个C程序库,就经过loadlib来加载。loadfile和loadlib都只是加载了代码,并无运行它们,为了运行代码,require会以模块名做为参数来调用这些代码。it
若是lua文件和C程序库都找不到,怎么办?咱们试一下,随便require一个东西,好比:
require "demo" no field package.preload['demo'] no file '.\demo.lua' no file 'C:\Program Files (x86)\Lua\5.1\lua\demo.lua' no file 'C:\Program Files (x86)\Lua\5.1\lua\demo\init.lua' no file 'C:\Program Files (x86)\Lua\5.1\demo.lua' no file 'C:\Program Files (x86)\Lua\5.1\demo\init.lua' no file 'C:\Program Files (x86)\Lua\5.1\lua\demo.luac' no file '.\demo.lua' no file 'C:\Program Files (x86)\Lua\5.1\lua\demo.lua' no file 'C:\Program Files (x86)\Lua\5.1\lua\demo\init.lua' no file 'C:\Program Files (x86)\Lua\5.1\demo.lua' no file 'C:\Program Files (x86)\Lua\5.1\demo\init.lua' no file 'D:\Game\5.1\lua\demo.luac' no file '.\demo.dll' no file '.\demo51.dll' no file 'C:\Program Files (x86)\Lua\5.1\demo.dll' no file 'C:\Program Files (x86)\Lua\5.1\demo51.dll' no file 'C:\Program Files (x86)\Lua\5.1\clibs\demo.dll' no file 'C:\Program Files (x86)\Lua\5.1\clibs\demo51.dll' no file 'C:\Program Files (x86)\Lua\5.1\loadall.dll' no file 'C:\Program Files (x86)\Lua\5.1\clibs\loadall.dll'
找不到就会提示报错!
package.loaded是什么?
require会将返回值存储到table package.loaded中;若是加载器没有返回值,require就会返回table package.loaded中的值。能够看到,咱们上面的代码中,模块没有返回值,而是直接将模块名赋值给table package.loaded了。这说明什么,package.loaded这个table中保存了已经加载的全部模块。如今咱们就能够看看require究竟是如何加载的呢?
1.先判断package.loaded这个table中有没有对应模块的信息;
2.若是有,就直接返回对应的模块,再也不进行第二次加载;
3.若是没有,就加载,返回加载后的模块。
1.从require传入的参数中获取模块名;
2.创建一个空table;
3.在全局环境_G中添加模块名对应的字段,将空table赋值给这个字段;
4.在已经加载table中设置该模块;
5.设置环境变量。
就是这几步,在每个模块的定义以前都须要加上,是否是有点麻烦,在Lua5.1中提供了一个新函数module,它包括了以上这些步骤完成的功能。