標籤:lua require module
一、模組機制module
1.什麼是module
對於使用者來說,一個module相當於一個so庫。模組的主要目標是實現代碼的共用。
2.如何編寫module
lua是通過table來實現模組的,典型的寫法如下。
local M = {} ---- 通常是加local的,如果不加,則M預設註冊到_G中,require後,即使不return也可以直接使用M。加了local是局部變數,需要顯示的return一下。
M.print = function(...)
print(...)
end
return M
二、require機制
1.require實現原理:
function require(name)
if not packge.loaded[name] then ---- 避免重複載入
local loader = findloader(name) ---- 如果是so,就以loadlib方式負載檔案,如果是lua檔案,就以loadfile方式負載檔案。
if loader == nil then
error("unable to load module " .. name)
end
package.loaded[name] = true ---- 將模組標記為以載入,我們有時候會看到require返回true的現象,是由於被調用的模組,沒有顯示的執行package.loaded[modname] = M或者給出return M這樣的傳回值。
local res = loader(name) ---- require會以name作為入參來執行該檔案,如果有返回結果,就將返回結果儲存在package.loaded[name]中,如果沒有返回結果,就直接返回package.loaded[name]。如果我們在被調用的檔案中直接寫明return 1。則調用者的require的返回結果就是1。但是只要我們顯示的在require檔案中寫明了_G[modname] = M,我們仍然可以在require之後,直接使用M作為名字來調用,是由於將M加入到了_G中。
if res ~= nil then
package.loaded[name] = res
end
end
return package.loaded[name]
end
2.require實現解析:
傳參: require會將模組名作為參數傳遞給模組
傳回值:如果一個模組沒有傳回值的話,require就會返回package.loaded[modulename]作為傳回值。
------------------------example---------------------
舉例:
pa.lua:
local modname = ...
local M = {}
_G[modname] = M
package.loaded[modname] = M
function M.print_mob()
print(modname)
end
mob.lua:
require "pa"
pa.print_mob()
執行結果:
lua mob.lua
pa
------------------------------------------------------------
分析:
pa.lua中的modname接收的是require傳遞過來的參數,將其加入到全域環境變數_G中,相當於動態建立了一個modname的表(注意:表名的賦值實際上是引用,相當於C語言中的指標,即使是傳參也會有相同的效果)。我們經常local m = require "mdname",實際上是將產生的表進行了重新命名,但是本質上還是mdname這個表。
pa.lua中的return M我們沒有顯示聲明,由package.loaded[modulename]來代替,通過require實現機制可以看到,這時候傳回值應該是true。
三、環境
lua用_G一張表儲存了全域資料(變數,函數和表等)。
如上分析,我們定義一個module,如果不加local,則它是一個註冊在全域下的表。我們通過加local避免了它在汙染全域資料表空間,只在本檔案生效。如果我們沒有將其註冊到_G下,在其他檔案是無法直接通過他的原始名字來訪問的。不便利的地方,每個函數前面都要帶M,M的下的函數相互訪問也要帶M頭。
解決方案:通過setfenv
local modname = ...
local M = {}
_G[modname] = M
package.loaded[modname] = M
setfenv(1, M)
後續的函數直接定義名字,因為他們的環境空間已經由_G改為了M。
如果要使用全域函數,則可以本地額外增加一條local _G = _G或者setmetatable(M, {__index = G})。
更好的方法是在setfenv之前將需要的函數都儲存起來,local sqrt = math.sqrt
四、module函數
local M = {}
_G[modname] = M
package.loaded[modname] = M
<set for external access: eg setmetatable(M, {__index = _G})>
setfenv(1, M)
等同於module(modname)。
預設情況下,module不提供外部存取,如果要訪問外部變數,兩種方法:
1.在聲明module之前,local 變數 = 外部變數
2.使用module(modname, package.seeall), 等價於setmetatable(M, __index = _G)
lua require與module