第一版是為 Lua 5.0 所寫的。雖然在很大程度上仍然適用於後續版本,但仍有一些差異。
第四版針對 Lua 5.3,可在 Amazon 和其他書店購買。
購買這本書,您也可以協助 支援 Lua 專案


13.4.1 – __index 元方法

我之前說過,當我們存取表格中不存在的欄位時,結果會是 nil。這句話沒錯,但並非全部。實際上,此類存取會觸發直譯器尋找 __index 元方法:如果沒有此方法(通常如此),則存取結果會是 nil;否則,元方法會提供結果。

這裡的典型範例是繼承。假設我們要建立數個描述視窗的表格。每個表格都必須描述數個視窗參數,例如位置、大小、色彩配置等。所有這些參數都有預設值,因此我們希望建立視窗物件,只提供非預設參數。第一個替代方案是提供一個建構函式,填入不存在的欄位。第二個替代方案是安排新的視窗從原型視窗繼承任何不存在的欄位。首先,我們宣告原型和一個建構函式函數,建立共用元表格的新視窗

    -- create a namespace
    Window = {}
    -- create the prototype with default values
    Window.prototype = {x=0, y=0, width=100, height=100, }
    -- create a metatable
    Window.mt = {}
    -- declare the constructor function
    function Window.new (o)
      setmetatable(o, Window.mt)
      return o
    end
現在,我們定義 __index 元方法
    Window.mt.__index = function (table, key)
      return Window.prototype[key]
    end
在該程式碼之後,我們建立一個新視窗,並查詢不存在的欄位
    w = Window.new{x=10, y=20}
    print(w.width)    --> 100
當 Lua 偵測到 w 沒有要求的欄位,但有一個具有 __index 欄位的元表格時,Lua 會呼叫此 __index 元方法,並以 w(表格)和 "width"(不存在的鍵)作為引數。然後,元方法會使用指定的鍵索引原型,並傳回結果。

__index 元方法用於繼承非常普遍,因此 Lua 提供了一個捷徑。儘管名稱為 __index,但 __index 元方法不需要是函數:它可以是一個表格。當它是函數時,Lua 會使用表格和不存在的鍵作為其引數來呼叫它。當它是表格時,Lua 會在該表格中重新執行存取。因此,在我們之前的範例中,我們可以將 __index 宣告為

    Window.mt.__index = Window.prototype
現在,當 Lua 尋找元表的 __index 欄位時,它會找到 Window.prototype 的值,它是一個表格。因此,Lua 會重複在這個表格中的存取,也就是執行等同於
    Window.prototype["width"]
這會給出想要的結果。

將表格用作 __index 元方法提供了一個實作單一繼承的便宜且簡單的方法。函式雖然比較昂貴,但提供了更大的彈性:我們可以實作多重繼承、快取和許多其他變化。我們將在 第 16 章 討論這些繼承形式。

當我們想要存取表格而不呼叫其 __index 元方法時,我們會使用 rawget 函式。呼叫 rawget(t,i) 會對表格 t 進行原始存取。執行原始存取不會加速您的程式碼(函式呼叫的開銷會抵消您可能獲得的任何好處),但有時您需要它,我們稍後會看到。