第一版是为 Lua 5.0 编写的。虽然在很大程度上仍然适用于更高版本,但还是有一些差异。
第四版针对 Lua 5.3,可在 Amazon 和其他书店购买。
购买本书还可以帮助 支持 Lua 项目


16 – 面向对象编程

Lua 中的表在多个方面都是对象。与对象类似,表具有状态。与对象类似,表具有独立于其值的标识(“自性”);具体而言,具有相同值的两个对象(表)是不同的对象,而一个对象可以在不同时间具有不同的值,但它始终是同一个对象。与对象类似,表具有独立于创建者或创建位置的生命周期。

对象有自己的操作。表也可以有操作

    Account = {balance = 0}
    function Account.withdraw (v)
      Account.balance = Account.balance - v
    end
此定义创建一个新函数,并将其存储在 Account 对象的 withdraw 字段中。然后,我们可以像这样调用它
    Account.withdraw(100.00)

这种函数几乎是我们所说的“方法”。但是,在函数中使用全局名称 Account 是一种不好的编程习惯。首先,此函数仅适用于此特定对象。其次,即使对于此特定对象,函数也仅在对象存储在该特定全局变量中时才有效;如果我们更改此对象的名称,withdraw 将不再起作用

    a = Account; Account = nil
    a.withdraw(100.00)   -- ERROR!
这种行为违反了对象具有独立生命周期的前述原则。

一种更灵活的方法是对操作的“接收者”进行操作。为此,我们必须为方法定义一个额外的参数,该参数告诉方法它必须在哪个对象上进行操作。此参数通常具有名称 selfthis

    function Account.withdraw (self, v)
      self.balance = self.balance - v
    end
现在,当我们调用方法时,我们必须指定它必须在哪个对象上进行操作
    a1 = Account; Account = nil
    ...
    a1.withdraw(a1, 100.00)   -- OK
通过使用 self 参数,我们可以对多个对象使用相同的方法
    a2 = {balance=0, withdraw = Account.withdraw}
    ...
    a2.withdraw(a2, 260.00)

在任何面向对象语言中,使用self 参数都是一个中心点。大多数面向对象语言都将这种机制部分地隐藏在程序员的视线之外,这样程序员就不必声明这个参数(尽管她仍然可以在方法内部使用名称selfthis)。Lua 也可以使用冒号运算符来隐藏这个参数。我们可以将前面的方法定义重写为

    function Account:withdraw (v)
      self.balance = self.balance - v
    end
并将方法调用重写为
    a:withdraw(100.00)
冒号的作用是在方法定义中添加一个额外的隐藏参数,并在方法调用中添加一个额外的参数。冒号只是一个语法工具,尽管它很方便;这里实际上没有什么新东西。我们可以使用点语法定义一个函数,并使用冒号语法调用它,反之亦然,只要我们正确处理额外的参数即可
    Account = { balance=0,
                withdraw = function (self, v)
                             self.balance = self.balance - v
                           end
              }
    
    function Account:deposit (v)
      self.balance = self.balance + v
    end
    
    Account.deposit(Account, 200.00)
    Account:withdraw(100.00)

现在我们的对象有了标识、状态和针对此状态的操作。它们仍然缺少类系统、继承和私有性。让我们解决第一个问题:如何创建具有类似行为的多个对象?具体来说,我们如何创建多个帐户?