Lua 中 metatable 是一個普通的 table,但其主要有以下幾個功能:
1.定義算術(shù)操作符和關(guān)系操作符的行為
2.為 Lua 函數(shù)庫提供支持
3.控制對 table 的訪問
Metatables 定義操作符行為
Metatable 能夠被用于定義算術(shù)操作符和關(guān)系操作符的行為。例如:Lua 嘗試對兩個 table 進行加操作時,它會按順序檢查這兩個 table 中是否有一個存在 metatable 并且這個 metatable 是否存在 __add 域,如果 Lua 檢查到了這個 __add 域,那么會調(diào)用它,這個域被叫做 metamethod。
Lua 中每個 value 都可以有一個 metatable(在 Lua 5.0 只有 table 和 userdata 能夠存在 metatable)。每個 table 和 userdata value 都有一個屬于自己的 metatable,而其他每種類型的所有 value 共享一個屬于本類型的 metatable。在 Lua 代碼中,通過調(diào)用 setmetatable 來設(shè)置且只能設(shè)置 table 的 metatable,在 C/C++ 中調(diào)用 Lua C API 則可以設(shè)置所有 value 的 metatable。默認的情況下,string 類型有自己的 metatable,而其他類型則沒有:
Metamethod 的參數(shù)為操作數(shù)(operands),例如:
每個算術(shù)操作符有對應(yīng)的 metamethod:
| + | __add |
| * | __mul |
| - | __sub |
| / | __div |
| - | __unm (for negation) |
| % | __mod |
| ^ | __pow |
對于連接操作符有對應(yīng)的 metamethod:__concat
同樣,對于關(guān)系操作符也都有對應(yīng)的 metamethod:
| == | __eq |
| < | __lt |
| <= | __le |
其他的關(guān)系操作符都是用上面三種表示:
a ~= b 表示為 not (a == b)
a > b 表示為 b < a
a >= b 表示為 b <= a
和算術(shù)運算符不同的是,關(guān)系運算符用于比較擁有不同的 metamethod(而非 metatable)的兩個 value 時會產(chǎn)生錯誤,例外是比較運算符,擁有不同的 metamethod 的兩個 value 比較的結(jié)果是 false。
不過要注意的是,在整數(shù)類型的比較中 a <= b 可以被轉(zhuǎn)換為 not (b < a),但是如果某類型的所有元素并未適當(dāng)排序,此條件則不一定成立。例如:浮點數(shù)中 NaN(Not a Number)表示一個未定義的值,NaN <= x 總是為 false 并且 x < NaN 也總為 false。
為 Lua 函數(shù)庫提供支持
Lua 庫可以定義和使用的 metamethod 來完成一些特定的操作,一個典型的例子是 Lua Base 庫中 tostring 函數(shù)(print 函數(shù)會調(diào)用此函數(shù)進行輸出)會檢查并調(diào)用 __tostring metamethod:
另外一個例子是 setmetatable 和 getmetatable 函數(shù),它們定義和使用了 __metatable 域。如果你希望設(shè)定的 value 的 metatable 不被修改,那么可以在 value 的 metatable 中設(shè)置 __metatable 域,getmetatable 將返回此域,而 setmetatable 則會產(chǎn)生一個錯誤:
看一個完整的例子:
控制 table 的訪問
__index metamethod
在我們訪問 table 的不存在的域時,Lua 會嘗試調(diào)用 __index metamethod。__index metamethod 接受兩個參數(shù) table 和 key:
__index 域也可以是一個 table,那么 Lua 會嘗試在 __index table 中訪問對應(yīng)的域:
我們通過 __index 可以容易的實現(xiàn)單繼承(類似于 JavaScrpit 通過 prototype 實現(xiàn)單繼承),如果 __index 是一個函數(shù),則可以實現(xiàn)更加復(fù)雜的功能:多重繼承、caching 等。我們可以通過 rawget(t, i) 來訪問 table t 的域 i,而不會訪問 __index metamethod,注意的是,不要太指望通過 rawget 來提高對 table 的訪問速度(Lua 中函數(shù)的調(diào)用開銷遠遠大于對表的訪問的開銷)。
__newindex metamethod
如果對 table 的一個不存在的域賦值時,Lua 將檢查 __newindex metamethod:
1.如果 __newindex 為函數(shù),Lua 將調(diào)用函數(shù)而不是進行賦值
2.如果 __newindex 為一個 table,Lua 將對此 table 進行賦值
如果 __newindex 為一個函數(shù),它可以接受三個參數(shù) table key value。如果希望忽略 __newindex 方法對 table 的域進行賦值,可以調(diào)用 rawset(t, k, v)
結(jié)合 __index 和 __newindex 可以實現(xiàn)很多功能,例如:
1.OOP
2.Read-only table
3.Tables with default values
Read-only table
有時候,我們需要為 table 設(shè)定一個唯一的 key,那么可以使用這樣的技巧:
新聞熱點
疑難解答
圖片精選