Metatables

*Konten ini diterjemahkan menggunakan AI (Beta) dan mungkin mengandung kesalahan. Untuk melihat halaman ini dalam bahasa Inggris, klik di sini.

Metatables memungkinkan tabel menjadi lebih kuat dari sebelumnya. Mereka dilampirkan ke data dan berisi nilai yang disebut metamethod. Metamethod diaktifkan ketika suatu tindakan tertentu digunakan dengan data yang dilampirkan.

Manipulasi metatables

Dua fungsi utama untuk menambahkan dan menemukan metatable dari sebuah tabel adalah setmetatable() dan getmetatable().

local x = {}
local metaTable = {} -- Metatables juga adalah tabel!
setmetatable(x, metaTable) -- Berikan x metatable yang disebut metaTable!
print(getmetatable(x)) --> table: [alamat memori heksadesimal]

Fungsi setmetatable() juga mengembalikan tabel yang Anda atur metatable-nya, jadi kedua skrip ini melakukan hal yang sama:

local x = {}
setmetatable(x, {})
local x = setmetatable({}, {})

Metamethods

Metamethod adalah fungsi yang disimpan di dalam metatable. Mereka dapat digunakan untuk memanggil tabel, menambahkan tabel, bahkan membagi tabel juga. Berikut adalah daftar metamethod yang tersedia:

MetodeDeskripsi
__index(table, index)Diaktifkan ketika table[index] diindeks, jika table[index] adalah nil. Juga dapat diatur ke tabel, dalam hal ini tabel tersebut akan diindeks.
__newindex(table, index, value)Diaktifkan ketika table[index] mencoba diatur (table[index] = value), jika table[index] adalah nil. Juga dapat diatur ke tabel, dalam hal ini tabel tersebut akan diindeks.
__call(table, ...)Diaktifkan ketika tabel dipanggil seperti fungsi, ... adalah argumen yang diteruskan.
__concat(table, value)Diaktifkan ketika operator penggabungan .. digunakan pada tabel.
__unm(table)Diaktifkan ketika operator unary digunakan pada tabel.
__add(table, value)Operator penjumlahan +.
__sub(table, value)Operator pengurangan .
__mul(table, value)Operator perkalian *.
__div(table, value)Operator pembagian /.
__idiv(table, value)Operator pembagian lantai //.
__mod(table, value)Operator modulus %.
__pow(table, value)Operator eksponensial ^.
__tostring(table)Diaktifkan ketika tostring dipanggil pada tabel.
__metatableJika ada, mengunci metatable sehingga getmetatable() akan mengembalikan ini alih-alih metatable dan setmetatable() akan menghasilkan kesalahan. Nilai non-fungsi.
__eq(table, value)Operator sama dengan ==¹
__lt(table, value)Operator kurang dari <¹
__le(table, value)Operator kurang dari atau sama dengan <=¹
__modeDigunakan dalam tabel lemah, menyatakan apakah kunci dan/atau nilai dari tabel adalah lemah. Lihat Gunakan tabel lemah.
__len(table)Diaktifkan ketika operator panjang # digunakan pada objek.
__iter(table)Digunakan untuk menunjukkan iterator kustom saat menggunakan iterasi umum.

Perlu dicatat bahwa saat menulis fungsi untuk metamethod aritmatika atau relasional, dua parameter fungsi dapat dipertukarkan antara tabel yang mengaktifkan metamethod dan nilai lainnya. Misalnya, saat melakukan operasi vektor dengan skalar, pembagian tidak komutatif. Oleh karena itu, jika Anda menulis metamethod untuk kelas vector2 Anda sendiri, Anda harus berhati-hati untuk memperhitungkan kedua skenario.

local vector2 = {__type = "vector2"}
local mt = {__index = vector2}
function mt.__div(a, b)
if type(a) == "number" then
-- a adalah skalar, b adalah vektor
local scalar, vector = a, b
return vector2.new(scalar / vector.x, scalar / vector.y)
elseif type(b) == "number" then
-- a adalah vektor, b adalah skalar
local vector, scalar = a, b
return vector2.new(vector.x / scalar, vector.y / scalar)
elseif (a.__type and a.__type == "vector2" and b.__type and b.__type == "vector2") then
-- baik a maupun b adalah vektor
return vector2.new(a.x / b.x, a.y / b.y)
end
end
function mt.__tostring(t)
return t.x .. ", " .. t.y
end
function vector2.new(x, y)
local self = setmetatable({}, mt)
self.x = x or 0
self.y = y or 0
return self
end
local a = vector2.new(10, 5)
local b = vector2.new(-3, 4)
print(a / b) -- -3.3333333333333, 1.25
print(b / a) -- -0.3, 0.8
print(2 / a) -- 0.2, 0.4
print(a / 2) -- 5, 2.5

Penggunaan

Ada banyak cara untuk menggunakan metatables.

Terapkan operator minus unary pada tabel

Metamethod __unm menangani operator minus unary. Gunakan untuk menegasikan setiap nilai dalam tabel:

local metatable = {
__unm = function(t) -- __unm adalah untuk operator - unary
local negated = {}
for key, value in t do
negated[key] = -value -- negasikan semua nilai dalam tabel ini
end
return negated -- kembalikan tabel
end
}
local table1 = setmetatable({10, 11, 12}, metatable)
print(table.concat(-table1, "; ")) --> -10; -11; -12

Indeks nilai yang hilang dari tabel lain

Berikut adalah cara menarik untuk mendeklarasikan sesuatu menggunakan __index:

local metatable = {
__index = {x = 1}
}
local t = setmetatable({}, metatable)
print(t.x) --> 1

__index diaktifkan ketika x diindeks dalam tabel dan tidak ditemukan. Luau kemudian mencari melalui tabel __index untuk indeks yang disebut x, dan, setelah menemukannya, mengembalikannya.

Panggil tabel seperti fungsi

Metatables dapat melakukan lebih dari sekadar menyediakan nilai yang hilang. Ambil contoh ini:

local t = {10, 20, 30}
print(t(5))

Biasanya Anda tidak dapat memanggil tabel, tetapi dengan metatables Anda dapat:

local metatable = {
__call = function(t, param)
local sum = {}
for i, value in ipairs(t) do
sum[i] = value + param -- Tambahkan argumen (5) ke nilai, lalu tempatkan di tabel baru (t).
end
return unpack(sum) -- Kembalikan nilai tabel individu
end
}
local t = setmetatable({10, 20, 30}, metatable)
print(t(5)) --> 15 25 35

Terapkan operator penjumlahan pada tabel

Anda juga dapat melakukan lebih banyak hal, seperti menambahkan tabel:

local table1 = {10, 11, 12}
local table2 = {13, 14, 15}
for k, v in table1 + table2 do
print(k, v)
end

Ini akan menghasilkan kesalahan yang mengatakan bahwa Anda mencoba melakukan aritmatika pada tabel, tetapi itu berhasil ketika dicoba dengan metatable:

local metatable = {
__add = function(t1, t2)
local sum = {}
for key, value in t1 do
sum[key] = value
end
for key, value in t2 do
if sum[key] then
sum[key] += value
else
sum[key] = value
end
end
return sum
end
}
local table1 = setmetatable({10, 11, 12}, metatable)
local table2 = setmetatable({13, 14, 15}, metatable)
for k, v in table1 + table2 do
print(k, v)
end

Atur nilai tanpa memanggil metamethods

Saat bermain dengan metatables, Anda mungkin mengalami beberapa masalah. Jika Anda perlu menggunakan metamethod __index untuk membuat nilai baru dalam tabel, tetapi metatable tabel tersebut juga memiliki metamethod __newindex, Anda akan ingin menggunakan fungsi bawaan Luau rawset() untuk mengatur nilai tanpa memanggil metamethod. Ambil contoh kode berikut sebagai contoh apa yang terjadi jika Anda tidak menggunakan fungsi ini.

local t = setmetatable({}, {
__index = function(self, i)
self[i] = i * 10
return self[i]
end,
__newindex = function(self, i, v)
-- Jangan atur nilai ke tabel dengan cara normal
end
})
print(t[1]) -- Menyebabkan overflow stack

Overflow stack terjadi ketika Anda mencoba memanggil fungsi dari dirinya sendiri terlalu banyak kali. Dalam fungsi __index di atas, self[i] diatur ke suatu nilai, jadi ketika sampai ke baris berikutnya, self[i] seharusnya ada dan tidak akan memanggil metamethod __index. Masalahnya adalah bahwa __newindex tidak membiarkan Anda mengatur nilai. Kehadirannya menghentikan nilai dari ditambahkan ke tabel dengan metode standar t[i] = v. Untuk melewati ini, gunakan fungsi rawset():

local t = setmetatable({}, {
__index = function(self, i)
rawset(self, i, i * 10)
return self[i]
end,
__newindex = function(self, i, v)
-- Jangan atur nilai ke tabel dengan cara normal
end
})
print(t[1]) --> 10

Gunakan tabel lemah

Variabel yang menyimpan tabel, fungsi, thread, dan userdata, termasuk Instances, menyimpan referensi ke objek-objek tersebut alih-alih salinan dari mereka. Menetapkan objek ke variabel lain membuat referensi lain ke objek yang sama:

local map = {key = 1}
local mapReference = map
print(map == mapReference) --> true
print(map == {key = 1}) --> false
local function func() end
local funcReference = func
print(func == funcReference) --> true
local part = Instance.new("Part")
local partReference = part
print(part == partReference) --> true

Objek yang tidak lagi dapat dijangkau melalui referensi kuat menjadi memenuhi syarat untuk pengumpulan sampah. Mengatur satu variabel ke nil tidak membuat objek memenuhi syarat untuk pengumpulan sampah jika referensi kuat lainnya masih menunjuk ke objek tersebut:

local child = {}
local parent = {Child = child}
child = nil
print(parent.Child) --> table: [alamat memori heksadesimal]

Meskipun child tidak lagi merujuk ke tabel anak, parent.Child masih melakukannya. Selama tabel induk tetap dapat dijangkau, referensinya menjaga tabel anak tetap dapat dijangkau juga.

Sebuah tabel lemah menyimpan kunci, nilai, atau keduanya tanpa mencegah pengumpul sampah dari mengambil objek yang tidak dapat dijangkau. Atur field __mode dari metatable tabel untuk mengontrol referensi mana yang lemah:

  • "k" membuat kunci lemah.
  • "v" membuat nilai lemah.
  • "kv" atau "vk" membuat baik kunci maupun nilai lemah.

Contoh kode berikut membuat tabel dengan nilai lemah:

local children = setmetatable({}, {
__mode = "v",
})
do
local child = {}
children.example = child
end

Setelah blok do berakhir, nilai yang disimpan dalam children.example tidak memiliki referensi kuat dan menjadi memenuhi syarat untuk pengumpulan sampah. Ketika pengumpul sampah mengambil tabel anak, Luau menghapus entri dari children. Pengumpulan sampah bersifat nondeterministik, jadi jangan bergantung pada kapan entri menghilang.

Mode apa pun yang Anda tinggalkan tetap kuat. Kunci dalam tabel __mode = "v" tetap menjadi referensi kuat, jadi objek yang disimpan sebagai kunci tetap dapat dijangkau dan entri-nya tetap ada dalam tabel.

Untuk Instances, pengumpulan sampah berlaku pada referensi Luau alih-alih pada objek itu sendiri. Mesin menyimpan data instance di luar VM Luau, jadi mengumpulkan referensi Luau tidak menghapus instance, dan instance yang tetap ada dalam model data tidak menjaga referensi Luau-nya tetap hidup. Untuk melepaskan instance, panggil Instance:Destroy() dan hapus setiap referensi yang tersisa ke dalamnya.

Gunakan tipe data set

Sebuah set adalah kumpulan item tanpa urutan dan tanpa elemen duplikat. Sebuah item ada atau tidak ada dalam set. Menggunakan metatables, Anda dapat membangun dan memanipulasi set dalam skrip Luau.

Metode dasar

Kode berikut mencakup fungsionalitas set dasar, memungkinkan Anda untuk membangun set baru, menambahkan dan menghapus item, memeriksa apakah set berisi item, dan mengeluarkan isi set.

local Set = {}
Set.__index = Set
-- Fungsi untuk membangun set dari daftar item opsional
function Set.new(items)
local newSet = {}
for key, value in items or {} do
newSet[value] = true
end
return setmetatable(newSet, Set)
end
-- Fungsi untuk menambahkan item ke set
function Set:add(item)
self[item] = true
end
-- Fungsi untuk menghapus item dari set
function Set:remove(item)
self[item] = nil
end
-- Fungsi untuk memeriksa apakah set berisi item
function Set:contains(item)
return self[item] == true
end
-- Fungsi untuk mengeluarkan set sebagai daftar yang dipisahkan koma untuk debugging
function Set:output()
local elems = {}
for key, value in self do
table.insert(elems, tostring(key))
end
print(table.concat(elems, ", "))
end

Buat set

Set baru dapat dibangun dengan memanggil Set.new() dengan array opsional item untuk ditambahkan.

local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})

Perlu dicatat bahwa berdasarkan definisi, set tidak memiliki konsep urutan.

Tambahkan item

Menambahkan item ke set yang ada dapat dilakukan melalui metode Set:add().

local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})
fruits:add("Mango")

Hapus item

Untuk menghapus item dari set, panggil Set:remove() dengan nama item.

local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})
fruits:remove("Orange")

Periksa item

Untuk memeriksa apakah set berisi item tertentu, gunakan Set:contains().

local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})
local result1 = fruits:contains("Cherry")
print(result1) -- true
local result2 = fruits:contains("Watermelon")
print(result2) -- false

Metode tambahan

Operasi lain yang berguna dapat diimplementasikan untuk set, memungkinkan Anda membandingkan item antara set, menggabungkan set, atau mengurangi satu set dari set lainnya.

Irisan

Ketika mempertimbangkan set sebagai diagram Venn, Anda dapat mendapatkan irisan dari dua set sebagai berikut, yang berarti item yang muncul di kedua set.

local function getIntersection(set1, set2)
local result = Set.new()
for key, value in set1 do
if set2:contains(key) then
result:add(key)
end
end
return result
end
local freshFruits = Set.new({"Mango", "Lemon", "Orange", "Cherry", "Lime", "Peach"})
local frozenFruits = Set.new({"Mango", "Peach", "Pineapple"})
local commonFruits = getIntersection(freshFruits, frozenFruits)
commonFruits:output() -- Mango, Peach

Gabungan

Anda dapat mendapatkan gabungan dari dua set dengan fungsi berikut, yang berarti kumpulan item dalam kedua set tanpa duplikat. Perhatikan bahwa fungsi ini menggunakan metode metatable __add untuk memberikan pintasan penjumlahan set1 + set2.

function Set:__add(otherSet)
local result = Set.new()
for entry in self do
result[entry] = true
end
for entry in otherSet do
result[entry] = true
end
return result
end
local sweetFruits = Set.new({"Apple", "Mango", "Cherry", "Peach"})
local sourFruits = Set.new({"Lemon", "Lime"})
local allFruits = sweetFruits + sourFruits
allFruits:output() -- Peach, Lime, Apple, Cherry, Lemon, Mango

Pengurangan

Anda dapat menghapus semua item dalam satu set dari item dalam set lainnya melalui fungsi berikut. Mirip dengan fungsi di atas, ini menggunakan metode metatable __sub untuk memberikan pintasan pengurangan set1 - set2.

function Set:__sub(otherSet)
local result = Set.new()
for entry in self do
result[entry] = true
end
for entry in otherSet do
result[entry] = nil
end
return result
end
local allFruits = Set.new({"Apple", "Lemon", "Mango", "Cherry", "Lime", "Peach"})
local sourFruits = Set.new({"Lemon", "Lime"})
local sweetFruits = allFruits - sourFruits
sweetFruits:output() -- Mango, Apple, Cherry, Peach
©2026 Roblox Corporation. Roblox, logo Roblox, dan Powering Imagination termasuk dalam merek dagang kami yang terdaftar dan tidak terdaftar di AS dan negara lainnya.