此第一版是針對 Lua 5.0 編寫的。雖然在後續版本中仍有很大的相關性,但有一些差異。
第四版針對 Lua 5.3,可在 Amazon 和其他書店購買。
購買書籍,您也可以幫助支援 Lua 專案


14.3 – 非全域環境

環境的問題之一在於它是全域的。您對其所做的任何修改都會影響程式的所有部分。例如,當您安裝元表來控制全域存取時,整個程式都必須遵循準則。如果您想要使用未宣告全域變數的函式庫,您就倒楣了。

Lua 5.0 透過允許每個函式擁有自己的環境來改善此問題。這乍聽之下可能很奇怪;畢竟,全域變數表的目標是要成為全域的。然而,在第 15.4 節中,我們將看到此功能允許多種有趣的建構,其中全域值仍可在任何地方使用。

您可以使用 setfenv 函式(設定函式環境)來變更函式的環境。它接收函式和新環境。除了函式本身之外,您也可以提供一個數字,表示給定堆疊層級中的活動函式。數字 1 表示目前的函式,數字 2 表示呼叫目前函式的函式(這對於撰寫變更其呼叫者環境的輔助函式很方便),依此類推。

第一次嘗試使用 setfenv 會慘不忍睹。程式碼

    a = 1   -- create a global variable
    -- change current environment to a new empty table
    setfenv(1, {})
    print(a)
會產生
    stdin:5: attempt to call global `print' (a nil value)
(您必須在單一區塊中執行該程式碼。如果您在互動模式中逐行輸入,每一行都是不同的函式,而對 setfenv 的呼叫只會影響其自己的行。)一旦您變更環境,所有全域存取都將使用此新表格。如果它為空,您就失去了所有全域變數,甚至是 _G。因此,您應該先使用一些有用的值來填充它,例如舊環境
    a = 1   -- create a global variable
    -- change current environment
    setfenv(1, {_G = _G})
    _G.print(a)      --> nil
    _G.print(_G.a)   --> 1
現在,當您存取「全域」_G 時,它的值是舊環境,其中您會找到欄位 print

您也可以使用繼承來填充新環境

    a = 1
    local newgt = {}        -- create new environment
    setmetatable(newgt, {__index = _G})
    setfenv(1, newgt)    -- set it
    print(a)          --> 1
在此程式碼中,新環境從舊環境繼承 printa。不過,任何指定都會傳遞到新表格。不會有誤改真正全域變數的危險,儘管您仍可透過 _G 來變更它們
    -- continuing previous code
    a = 10
    print(a)      --> 10
    print(_G.a)   --> 1
    _G.a = 20
    print(_G.a)   --> 20

當您建立一個新函數時,它會從建立它的函數繼承其環境。因此,如果一個區塊改變它自己的環境,它之後定義的所有函數都會共享這個相同的環境。這是建立命名空間的一個有用的機制,正如我們將在下一章看到的。