作为一个Lua新手,我花了一段时间才理解@Yu Hao的回答,所以我会尝试为其他初学者添加一些细节。如有错误请指正。
据我所知,x:someFunc() 之类的调用在 [*] 时有效:
-
x 有一个元表
- 并且元表有一个字段
__index
- 指向一个包含函数
someFunc的表。
正如于浩所指出的,字符串会自动获得一个指向表string的元表,例如:
th> s = 'test'
th> getmetatable(s)
{
__mod : function: 0x40c3cd30
__index :
{
upper : function: builtin#82
rep : function: builtin#79
split : function: 0x40ffe888
gfind : function: builtin#87
find : function: builtin#84
reverse : function: builtin#80
lower : function: builtin#81
len : function: 0x40af0b30
tosymbol : function: 0x40ffe8a8
myFunc : function: 0x41d82be0 -- note: this comes from our custom function string:myFunc()
dump : function: builtin#83
byte : function: builtin#76
char : function: builtin#77
gmatch : function: builtin#87
match : function: builtin#85
sub : function: builtin#78
gsub : function: builtin#88
format : function: builtin#89
}
}
所以在这种情况下s:myFunc() 会自动工作。为了对table 使用冒号语法,我们可以手动设置它的元表:
th> function enableColonForTable(t)
..> meta = {__index = table}
..> setmetatable(t, meta)
..> end
th> t = {}
th> enableColonForTable(t)
th> t:insert(1) -- works now!
另一个观察结果是,__index 是否指向与类型名称完全相同的表实际上并不重要。除了meta = {__index = table},我们还可以这样做:
th> arbitraryScope = {}
th> function arbitraryScope:test() return "something" end
th> t = {}
th> setmetatable(t, {__index = arbitraryScope})
{}
th> t:test()
something
这也是number 的关键区别。虽然存在名为string 和table 的现有表,但没有名为number 的现有表。这就是为什么甚至定义例如function number:abs() 以前失败过。但我们仍然可以让它工作:
th> number = {}
th> function number:abs() return math.abs(self) end
th> x = -123
th> debug.setmetatable(x, {__index = number})
-123
th> x:abs()
123
请注意,我们必须在这里使用debug.setmetatable 而不是setmetatable。两者的区别似乎是setmetatable 只为给定实例设置元表,而debug.setmetatable 为整个类型设置元表。显然设置了一个individual metatable for numbers is forbidden(无论如何也没有多大意义)。这意味着(与表格相比)新构建的数字现在默认具有给定的元表,所以这是可行的:
th> y = -42
th> y:abs()
42
[*] 更新
正如 Tom Blodget 所指出的,如果 x 本身用作命名空间,x:someFunc() 也可以工作,即它是一个带有方法字段 someFunc 的表。例如你可以做table:insert(1)。但现在命名空间(名为table 的表)作为self 传递,您将向命名空间添加数据:
th> print(getmetatable(table)) -- note: "table" does not have a metatable
nil
th> table:insert(1) -- yet a colon syntax call works
th> table
{
prune : function: 0x4156bde0
getn : function: 0x41eb0720
maxn : function: builtin#90
remove : function: 0x41eb08c8
foreachi : function: 0x41eb05b8
sort : function: builtin#93
concat : function: builtin#92
unpack : function: builtin#16
splice : function: 0x4156bdc0
foreach : function: 0x41eb0688
1 : 1
pack : function: builtin#94
insert : function: builtin#91
}