26. Method dan self

Kamu sudah sering memanggil fungsi yang tersimpan di tabel — string.format, table.insert, math.floor. Bab ini menunjukkan cara mendefinisikan fungsi milikmu sendiri yang melekat pada tabel tertentu, dan apa yang sebenarnya dilakukan sintaks titik dua (obj:method()) di balik layar. Titik dua ada di mana-mana di Roblox; memahaminya sekali akan menjelaskan banyak kode Roblox.

Fungsi pada tabel, cara panjang

Fungsi pada tabel hanyalah sebuah nilai di kunci string:

local counter = { count = 0 }

function counter.step(c)
    c.count = c.count + 1
end

counter.step(counter)
counter.step(counter)
counter.step(counter)
print(counter.count)   -- 3

Fungsi counter.step menerima tabel itu sendiri sebagai argumen pertama, lalu menggunakannya untuk memperbarui counter. Setiap pemanggilan harus mengoper counter secara eksplisit: counter.step(counter). Ini berfungsi, tapi terasa tidak praktis.

Pintasan titik dua

Titik dua di Lua melakukan dua hal sekaligus — semacam pemanis sintaks yang membuat gaya pemanggilan yang terikat pada tabel jadi lebih bersih.

Memanggil dengan : secara otomatis mengoper tabel sebagai argumen pertama:

counter:step()   -- exactly the same as counter.step(counter)

Mendefinisikan dengan : secara otomatis mendeklarasikan parameter bernama self yang menerima tabel:

function counter:step()
    self.count = self.count + 1
end

Definisi yang sama seperti sebelumnya, ditulis lebih ringkas. Gabungkan kedua pintasan:

local counter = { count = 0 }

function counter:step()
    self.count = self.count + 1
end

counter:step()
counter:step()
counter:step()
print(counter.count)   -- 3

Di dalam tubuh fungsi, self hanyalah nama parameter biasa. Tulis self.x untuk membaca sebuah field, self.x = ... untuk mengubahnya. Nama self adalah konvensi, bukan aturan — itu hanya parameter implisit yang dibuat oleh definisi dengan titik dua.

Buka exercises/26/01-counter.lua. File itu sudah punya counter:step. Tambahkan counter:reset yang mengatur self.count kembali ke 0, lalu panggil dari bagian bawah file.

Pemanggilan titik dua bawaan

Banyak fungsi bawaan menerima sintaks titik maupun titik dua. Kedua bentuk ini bisa dipertukarkan:

local name = "keiko"

print(string.upper(name))   -- KEIKO
print(name:upper())         -- KEIKO     -- same

name:upper() mengoper name sebagai argumen pertama ke string.upper. Ini berfungsi karena string membawa tautan implisit ke tabel string, sehingga method string apa pun bisa dipanggil sebagai s:method(). Kamu akan melihat ini di mana-mana dalam kode Lua sungguhan.

Objek game sederhana

Sintaks method sangat berguna ketika beberapa bagian state disatukan. Berikut seekor anjing dengan nama dan jumlah gonggongan yang tersisa:

local dog = {
    name = "Rex",
    barks_left = 3,
}

function dog:bark()
    if self.barks_left <= 0 then
        print(self.name .. " is hoarse.")
        return
    end
    print(self.name .. ": Woof!")
    self.barks_left = self.barks_left - 1
end

dog:bark()       -- Rex: Woof!
dog:bark()       -- Rex: Woof!
dog:bark()       -- Rex: Woof!
dog:bark()       -- Rex is hoarse.

Tabel dog menyimpan state; method mengubahnya di setiap pemanggilan. Ini adalah bagian dari pemrograman berorientasi objek yang kamu butuhkan untuk bab berikutnya, dan pola yang sama yang kamu lihat di Roblox: sebuah Instance (sebuah tabel) dengan method seperti :Destroy(), :GetChildren(), :Clone().

Kesalahan umum: mencampur dua bentuk

obj.method() dan obj:method() bukan pemanggilan yang sama. Titik tidak mengoper obj secara otomatis. Jika method didefinisikan dengan :, memanggil dengan . dan lupa mengoper obj membuat self menjadi nil:

dog.bark()        -- error: attempt to index a nil value (local 'self')

Solusinya cukup satu karakter: gunakan :.

Kesalahan sebaliknya — obj:method(obj) — mengoper obj dua kali. Yang pertama masuk ke self, yang kedua menjadi argumen tambahan. Biasanya diabaikan saja, tapi bisa membingungkan saat debugging.

PR

Soal 1 — Objek counter

Buka exercises/26/homework/01-counter-object.lua. Buat tabel counter dengan field dan method berikut:

  • counter.count — mulai dari 0.
  • counter:inc() — menambahkan 1.
  • counter:get() — mengembalikan nilai count saat ini.
  • counter:reset() — mengatur count kembali ke 0.

Panggil secara berurutan dan cetak nilainya di setiap langkah.

Soal 2 — Anjing yang menggonggong

Buka exercises/26/homework/02-dog.lua. Buat tabel dog dengan field name dan method :bark() yang mencetak <name>: Woof!. Buat anjing kedua dengan nama berbeda. Panggil keduanya :bark() dan pastikan masing-masing mencetak namanya sendiri.

Soal 3 — Objek kalkulator

Buka exercises/26/homework/03-calculator.lua. Buat tabel calc dengan:

  • calc.value — mulai dari 0.
  • calc:add(n) — menambahkan n ke calc.value.
  • calc:sub(n) — mengurangi.
  • calc:mul(n) — mengalikan.
  • calc:show() — mencetak nilai saat ini dengan label.

Rangkaikan beberapa pemanggilan dan pastikan nilainya diperbarui sesuai harapan.

Tantangan — Stack

Buka exercises/26/homework/04-stack.lua. Buat tabel stack yang berperilaku seperti stack:

  • stack.items — sebuah list.
  • stack:push(v) — menambahkan v ke atas.
  • stack:pop() — menghapus dan mengembalikan item teratas.
  • stack:peek() — mengembalikan item teratas tanpa menghapusnya (atau nil jika kosong).
  • stack:size() — mengembalikan jumlah item yang tersimpan.

Gunakan table.insert dan table.remove tanpa argumen kedua — itu akan push dan pop di akhir list, perilaku stack yang standar.

Bingung atau sudah selesai? Buka halaman solusi PR.