第一版是為 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)

現在,我們的物件具有身分、狀態和對此狀態的運算。它們仍然缺乏類別系統、繼承和私密性。讓我們解決第一個問題:我們如何建立具有類似行為的幾個物件?具體來說,我們如何建立幾個帳戶?