|
在lua中,表拥有一个标识:self。self类似于this指针,大多数面向对象语言都隐藏了这个机制,在编码时不需要显示的声明这个参数,就可以在方法内使用this(例如C++和C#)。在lua中,提供了冒号操作符来隐藏这个参数,例如:
local t = {a = 1, b = 2}
function t:Add()
return (self.a + self.b)
end
print(t:Add())冒号的作用有两个:1. 对于方法定义来说,会增加一个额外的隐藏形参(self);2. 对于方法调用来说,会增加一个额外的实参(表自身)
冒号只是一种语法机制,提供的是便利性,并没有引入任何新的东西。使用冒号完成的事情,都可以使用点语法来完成。看下面的例子:
local t = {a = 1, b = 2}
function t:Add()
return (self.a + self.b)
end
function t.Sub(self)
return (self.a - self.b)
end
print(t.Add(t))
print(t:Sub())既然点和冒号都可以完成同样的事情,那么除了便利性之外,还有没有其他区别呢?有,lua为冒号提供了独有的指令:SELF
我们以下面A和B两个等价的例子来说明:
-------------------------------------
---Sample A
local tA = {a = 1, b = 2}
function tA.Add(self)
return (self.a + self.b)
end
print(tA.Add(tA))
-------------------------------------
---Sample B
local tB = {a = 1, b = 2}
function tB:Add()
return (self.a + self.b)
end
print(tB:Add())对于例A,"print(tA.Add(tA))"生成的指令如下所示:
在指令序列中,首先获取全局环境中的print函数,接着获取tA表中的Add函数,然后通过MOVE指令将tA压入栈顶,因为tA将作为参数传入给Add函数。接着就是两条函数调用指令CALL,分别调用Add函数与print函数。总共需要5条指令。
SELF指令
在看例B生成的指令之前,我们先来了解下SELF指令。lua中的指令分为四类:iABC、iABx、iAsBx、iAx(这里我们不深入讨论指令,感兴趣的读者可以自行查阅资料 :))。SELF指令属于iABC类型,它会将表对象和函数拷贝到寄存器的连续两个位置中,寄存器索引由操作数A指定,表对象在寄存器中的位置由操作数B指定,操作数C指定了函数的位置(寄存器中或者常量表中)。
然后我们来看例B,"print(tB:Add())"生成的指令如下所示:
在指令序列中,首先获取全局环境中的print函数,接着SELF指令做了两件事情:获取tB表中的Add函数以及将tB压入栈顶,由此可见冒号语法会将表自身作为实参传入给Add函数。接着还是两条函数调用指令CALL,分别调用Add函数与print函数。总共需要4条指令。
总结
通过以上分析,可以了解到冒号语法除了提供便利性之外,还会为我们节省一条指令。虽然一条指令的开销几乎可以忽略不计,但性能优化不就是这样一点一滴抠出来的嘛,哈哈!
Happy Coding! :)
本文固定链接: 弘竣:Lua中的self
转载请注明: 弘竣 2020年03月22日 于 知乎 发表 |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|