如何簡單易懂地解釋 Lua 的元表(metatable)?知乎使用者2017-05-30 06:31:25

Lua 的 metatable 並不只是為了 OO 而存在,但 Lua 的面向物件機理,prototype based object orientation,的確是透過 metatable 而實現的。

簡單來說,prototype based OO 的根本特徵是不採用「類」這一層抽象,也就是說,所有物件都是沒有 class 的 instance。當一個 object 需要某個其他 object 已有的功能時,可以將後者設為自己的 prototype;同一個 object 可以同時作為幾個其他 object 共同的 prototype(但反之通常不成立);已指定 prototype 的 object 還可以成為其他 object 的 prototype;一個 object 可以以自己為 prototype。一系列 object 以此方式組成一串 prototype 鏈,在某個 object 上呼叫它並未定義的方法或屬性時,系統會上溯這條鏈,依次檢視每一級 prototype 是否有定義,直至找到或窮盡到鏈條終點為止。舉例而言,假設有三個維京人,甲會彈弓,乙會用劍,丙會持盾牌;甲給乙當 prototype,於是乙也會了彈弓;乙再給丙當 prototype,丙就成了可以持盾揮劍射彈弓的全能維京人。甲沒有 prototype(或者有一個什麼都不會的預設 prototype),丙則不是任何人的 prototype(但他可以是)。

具體到 Lua 這裡,這種「向上尋找 prototype」的機制,可以用 metatable 之中的 metamethod 完成。最簡單但並不是最簡潔的例子:

a = { foo = function() return ‘foo’ end }

b = { bar = function() return ‘bar’ end }

c = { __index = b }

setmetatable(a, c)

a。bar()

—— ‘bar’

將 table c 的 __index 定義為 table b,然後將 a 的 metatable 定為 c,就讓 b 變成了 a 的 prototype。而 c 作為 metatable 的角色其實可以融合到 b 或者 a 中:

a = { foo = function() return ‘foo’ end }

b = { bar = function() return ‘bar’ end }

b。__index = b

setmetatable(a, b)

a。bar()

—— ‘bar’

這樣一來,b 既是 a 的 prototype,也是 a 的 metatable。將 __index 指向另一個 table 大概是它最簡單也最普遍的用法,只是 Lua 官方教程中引入 __index 的部分寫得比較複雜,把它寫成了一個函式,意圖大概是考慮到大部分讀者對 prototype based OO 並不是很瞭解(那時候 JavaScript 還沒有現在這麼流行),所以用 metatable 來強行實現了一個看起來很接近 class-based 的 OO。

但使用 __index 實現 OO 只是 metatable 的用途之一。Metatable 的本質其實就是它的名字所意味的那樣:一張關於表的表。Table 裝著資料和功能,metatable 則裝著關於 table 的資料和功能。你可以把它想象成一個 table 的控制檯,改變 metatable 的內容,會改變 table 的特徵和行為。比如 __tostring 會改變 table 在轉換為字串時的輸出,__mode 可以控制 key / value 是否為 weak reference,__add / __sub / __mul / __div 可以定義兩個表互相加減乘除的時候究竟應該做什麼,__eq / __lt / __le 可以讓兩個表比較大小。Metatable 利用 Lua table 這個單一而靈活的資料結構緊湊而優雅地完成了其他語言裡使用 mixin、magic method、operator overload 實現的功能。

如何簡單易懂地解釋 Lua 的元表(metatable)?Cwood2018-01-22 00:16:54

魔法屬性

如何簡單易懂地解釋 Lua 的元表(metatable)?森坦森2018-04-26 15:08:13

簡單易懂的理解metatable,就要先摒棄metatable所能衍生的各種強大黑科技,直視它最初的解釋。

首先,lua中的每個值都可以有一個metatable,metatable就是一個表。

metatable定義了這個值執行某些特定操作時的行為。

比如,當你對一個值進行“相加”的操作時,lua會查詢這個值的metatable中是否存在“__add”為key的函式,如果存在,則呼叫它來執行這個相加操作。

以上摘自lua官方文件。

但是就是這麼個簡單的機制,可以用來實現各種lua黑科技,面向物件只是其中之一。

如何簡單易懂地解釋 Lua 的元表(metatable)?熊起2018-12-31 10:31:40

對metatable的支援是直接在luavm的虛擬機器裡實現的,大概這樣

switch(op_code)

case OP

ADD: if(getMetatable(t))

//call metatable。

_add

else

//add as number

從此可以看出設計lua的思路,metatable是語言的基礎特性,只是可以用來構成基於函式表覆蓋規則的繼承多型,也可以部分完成C++風格的運算子與過載。

我覺得可以當作運算子過載來理解,只不過因為lua型別系統和java C++等靜態型別不同,又使用table代替物件,所以透過對_

index附加一條“先在當前table查詢,沒有就用metatable。

__index”的規則, 就足以兼任一個鴨模型。

lua是強調緊湊的語言,能不加特性就不加,如果能用一兩項lua已有特性湊合模擬一個其他語言的特性,那就不會把這個特性加到lua裡

如何簡單易懂地解釋 Lua 的元表(metatable)?Younccat2019-08-02 18:29:01

如果你寫過 JS,你會發現它很有 __

proto_

_ 的味道