前言編程
從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,它包括了以上這些步驟完成的功能。