LUA中的冒号、点和self

在Lua编程中,经常会看到有时用点号定义一个table的成员函数,有时却用冒号,在调用的时候也是如此。那么点号和冒号在使用上有什么区别呢?它们与self之间又是什么样的关系呢?稍安勿躁,接下来谜底将一一为你揭开。

下面先看一个例子

Class = {}
Class.__index = Class
  
function Class.new(x,y)
    local cls = {}
    setmetatable(cls, Class)
    cls.x = x
    cls.y = y
    return cls
end
function Class:test()
-- 等价于
-- function Class.test(self)
    print(self.x,self.y)
end
  
  
object = Class.new(10,20)
 
object:test() --10 20
-- 等价于
object.test(object) --10 20

可以看到:

(1)定义的时候:Class:test()与 Class.test(self)是等价的;

(2)调用的时候:object:test() 与object.test(object)等价。

但是,一定要小心了,如果我们把例子中代码做一个小小的改动:

object = Class.new(10,20)

改为

object = Class:new(10,20)

猜一下最终的输出结果是什么?

最终的输出结果:

table: 00FFE788 10

table: 00FFE788 10

你是不是大吃一惊了,输出的结果跟我们的预期完全就是天壤之别。那为什么会得到这样的输出结果呢?深层次的原因是什么?下面一一道来。

在这里,我们在调用的时候将点号改为了冒号,使用冒号时,会隐性传递一个self参数,它表示的是调用对象,这样一来,Class:new(10, 20)这样的函数调用实际上有3个实参,分别是self,10,20,但是function Calss.new(x, y)这样用点号实现的函数定义实际上只有2个形参,这样传参的结果是

self ——> x

10 ——> y

20 ——> 丢弃

因此,最终我们打印出来的x和y的值就是self和10,而self实际就是table Class。如果我们把Class.new方法稍微修改一下如下:

function Class.new(self, x,y)

local cls = {}

setmetatable(cls, Class)

cls.x = x

cls.y = y

return cls

end

或者改为

function Class:new(x,y)

local cls = {}

setmetatable(cls, Class)

cls.x = x

cls.y = y

return cls

end

那么最终输出结果将会是我们预期的:

10 20

10 20

通过上述分析,我们可以看到,点号和冒号最主要的区别在于是否包含隐性参数self,使用冒号格式定义或调用函数方法时,Lua会默认把调用者对象隐藏起来了,虽然隐藏起来了,但是我们却不能忽略它的存在性,不管你相信不相信,它就在那里,不左不右。而使用点号格式定义或调用函数方法时,所有参数都是被显示地声明的,不会躲躲藏藏,有多少就是多少,因此,如果我们需要传递调用者对象,那么必须显示声明之。

下面来看几个例子

例1

a = { x = 1 }

function a.fun(self)

print(self.x)

end

a.fun(a) --输出1,将a自己做为参数传给fun函数的self

通过点号格式定义fun,并且通过点号调用函数fun。定义时显示给出self形参用于接收调用者对象;调用时必须显示给出调用者对象作为实参传入。

例2

function a:fun()

print(self.x)

end

a.fun(a)

通过冒号格式定义fun,并且通过点号调用fun。定义时隐藏了形参self,虽然我们看不到,但是self是客观存在的,因此,在函数内部我们可以直接使用self;调用时显示给出调用者对象作为实参传入。

例3

function a:fun()

print(self.x)

end

a:fun()

通过冒号格式定义和调用fun。定义时隐藏了形参self,调用时也隐藏了调用者对象这个实参,虽然我们看不到,但是a这个实参确确实实是被传递给了self。

上面讨论的都是函数方法的定义和调用,那么对于非函数的数据成员又是怎么样的呢?对于数据成员的访问我们是否也是可以通过冒号或点号的形式来完成呢?很遗憾地告诉你,答案是否定的。

看一个例子

girl = {money = 200}

function girl.goToMarket(girl ,someMoney)

girl.money = girl.money - someMoney

end

girl.goToMarket(girl ,100)

print(girl.money)

输出结果

100

girl = {money = 200}

function girl.goToMarket(girl ,someMoney)

girl.money = girl.money - someMoney

end

girl.goToMarket(girl ,100)

print(girl:money)

输出结果

error:function arguments expected near ')'

通过上面例子可以看到,对于非成员函数的数据成员的访问,可以用点号的形式,但是不能用冒号的形式。

综上所述,给出一条不成文的约定:

用lua进行面向对象的编程,成员方法的声明和调用统一用冒号形式,对于数据成员的调用全部用点号形式。