I’ve searched through multiple Dev Forum and YouTube posts about metatables and how they work. However, I haven’t seen much specifically talking about how self is passed.
I know that when you do this, self is automatically passed.
function mt:MyMethod()
-- ...
end
Specifically, my issue is the ItemAdded not being able to pass down the self value to the Connect function.
local superTable = setmetatable({
["_itemAddedFunctions"] = {},
["_itemRemovedFunctions"] = {}
}, {
__index = {
AddItem = function(self, item)
table.insert(self, item)
for _, f in ipairs(self._itemAddedFunctions) do
task.spawn(f)
end
end,
ItemAdded = {
Connect = function(self, f)
table.insert(self._itemAddedFunctions, f)
end
}
}})
local function test()
print("item added!")
end
superTable.ItemAdded:Connect(test)
superTable:AddItem("my item")
If you call a function of an object with a colon instead of a period, the object is passed implicitly as the first argument, self. Additionally, if you define the function of an object with a colon instead of a period, self will already be implicitly defined as the first argument.
For example, if we create an object as
local Object = {}
Object.__index = Object
function Object.new()
return setmetatable({}, Object)
end
local obj = Object.new()
then both of these lines of code are functionally identical:
obj:DoSomething() -- colon operator implicitly passes obj as first argument
obj.DoSomething(obj) -- period does not pass obj as first argument, we must pass it ourselves
and for defining the function, both of these methods will also be functionally identical:
function Object:DoSomething()
print(self)
end
function Object.DoSomething(self)
print(self)
end
local mt = {}
local superTable = setmetatable({
["_itemAddedFunctions"] = {},
["_itemRemovedFunctions"] = {}
}, mt)
mt.__index = {
AddItem = function(self, item)
table.insert(self, item)
for _, f in ipairs(self._itemAddedFunctions) do
task.spawn(f)
end
end,
ItemAdded = setmetatable({
Connect = function(self, f)
table.insert(self.super._itemAddedFunctions, f)
end
},
{
__index = {
super = superTable
}
}
)
}
local function test()
print("item added!")
end
superTable:AddItem("my item")
superTable.ItemAdded:Connect(test)
self will always refer to the table that a function is directly called from when the semicolon is used. ItemAdded:Connect() is the same as ItemAdded.Connect(ItemAdded). This is how it is intended to function - self is used to refer to the direct table a method was called on. However, to get the value of the table the table is nested within, we can split the setting into multiple lines so that we can refer to the superTable variable within the __index of the metatable. Then we can use another nested metatable to pass a reference value of the superTable into the ItemAdded table.
This is clearly a roundabout method but it gets the job done.
EDIT: Technically you don’t need to use a nested metastable if you are fine with just directly doing superTable instead of self.super in the Connect function.
Thank you so much for the clear explanations on how to get the self value.
I wanted to mark both of your solutions as solution but looks like Roblox can’t make that happen.
I will mark the last post as solution, since I was able to both understand what was going wrong and learn about self at the end of reading all of you all’s responses!