第一版為 Lua 5.0 所撰寫。儘管在後續版本中仍有很大的關聯性,但仍有一些差異。
第四版針對 Lua 5.3,可在 Amazon 和其他書店購買。
購買書籍,您也可以 贊助 Lua 專案


14.2 – 宣告全域變數

Lua 中的全域變數不需要宣告。儘管這對小型程式很方便,但在大型程式中,一個簡單的拼寫錯誤可能會導致難以找到的錯誤。不過,如果我們願意,可以改變這種行為。由於 Lua 將其全域變數保存在常規表格中,因此我們可以使用元表格來改變其在存取全域變數時的行為。

第一種方法如下

    setmetatable(_G, {
      __newindex = function (_, n)
        error("attempt to write to undeclared variable "..n, 2)
      end,
      __index = function (_, n)
        error("attempt to read undeclared variable "..n, 2)
      end,
    })
在該程式碼之後,任何嘗試存取不存在的全域變數的行為都會觸發錯誤
    > a = 1
    stdin:1: attempt to write to undeclared variable a

但是,我們如何宣告新變數?使用 rawset,它會繞過元方法

    function declare (name, initval)
      rawset(_G, name, initval or false)
    end
使用 or 搭配 false 可確保新的全域變數始終會取得與 nil 不同的值。請注意,您應該在安裝存取控制之前定義此函式,否則會出現錯誤:畢竟,您正在嘗試建立一個新的全域變數 declare。有了這個函式,您就可以完全控制您的全域變數
    > a = 1
    stdin:1: attempt to write to undeclared variable a
    > declare"a"
    > a = 1             -- OK

但是現在,要測試變數是否存在,我們不能只將它與 nil 進行比較;如果它是 nil,存取會擲出錯誤。相反地,我們使用 rawget,它會避免元方法

    if rawget(_G, var) == nil then
      -- `var' is undeclared
      ...
    end

要將該控制變更為允許具有 nil 值的全域變數並不困難。我們只需要一個輔助表格來保留已宣告變數的名稱。每當呼叫元方法時,它就會在該表格中檢查變數是否未宣告。程式碼可能是這樣

    local declaredNames = {}
    function declare (name, initval)
      rawset(_G, name, initval)
      declaredNames[name] = true
    end
    setmetatable(_G, {
      __newindex = function (t, n, v)
        if not declaredNames[n] then
          error("attempt to write to undeclared var. "..n, 2)
        else
          rawset(t, n, v)   -- do the actual set
        end
      end,
      __index = function (_, n)
        if not declaredNames[n] then
          error("attempt to read undeclared var. "..n, 2)
        else
          return nil
        end
      end,
    })

對於這兩種解決方案,其負擔可以忽略不計。對於第一個解決方案,在正常運作期間永遠不會呼叫元方法。在第二個解決方案中,它們可能會被呼叫,但僅當程式存取包含 nil 的變數時。