1. 程式人生 > >lua require與module

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)