Set other properties before Parent after Instance.new

scripting
instance
guide
performance

#1

If you haven’t read the PSA about Instance.new, read that first, as this post will be discussing that.

Now, this doesn’t just mean “don’t use the parent argument for Instance.new”. I’ve seen quite a lot of people coding like this:

local part = Instance.new("Part")
part.Parent = workspace
part.Size = Vector3.new(10, 10, 1)
-- more properties being set...

This is still a performance waste and you haven’t improved anything.
The whole point of not using the parent argument for Instance.new is to set the properties of the instance before parenting it. As you can see in the code above, that’s not what it was doing.
In the PSA post referred to above, the author explained the steps that went through when using the parent argument:

The steps
  1. You create a Part with the CFrame of 0,0,0 and a default Size, and parent it to workspace.
  2. ROBLOX sets up property change listeners for replication, rendering, etc.
  3. ROBLOX queues a replication of a new instance with default values for the Size/CFrame
  4. ROBLOX updates physics contacts between this part and whatever is at the origin
  5. You set the part Size to 10,10,1
  6. ROBLOX queues a replication change for the Size
  7. ROBLOX updates physics contacts between this part and whatever is at the origin and overlaps with the part given the new dimensions

You’re going through the exact same steps even without using the parent argument because you’re still parenting the instance before setting all the properties.
The more efficient way to do this (which is also the point of the post) is to parent the instance after setting the other properties, as similarly shown in the referred post:

local part = Instance.new("Part")
part.Size = Vector3.new(10, 10, 1)
-- more properties being set...
part.Parent = workspace

If you’re just going to set the Parent property directly after creating the instance, it would basically be the same thing as using the parent argument, performance wise.

Some people might think that this post is unnecessary as the referred post already explains this and that’s what I thought at first too, but I kept seeing more and more people misunderstanding it and I had to make a topic about it.

TL;DR: The parent of new instances should be the last property to be set, not first.

By the way, this is my first DevForum post, and I’ve been looking forward to it.


#2

Thanks for clearing this up! It’s a common misconception that simply avoiding the parent argument of Instance.new will fix the performance.


#3

very epic


#4

It would be cool if Instance.new were to let you pass something like a table to set all the properties you want before the Instance is created. I’ve wanted something like that since forever and this is just a better reason for it.


#5

I’ve just created this function that should work to serve that purpose:

-- Instance new(string class_name, [table] property_values)
-- Instantiate class of class_name, assign properties to values if provided, and return the
-- instance. property_values should be a dictionary with properties as keys and values as values.
local function new(class_name, property_values)
    assert(type(class_name) == "string", "bad argument #1 to new")
    assert(type(property_values) == "table" or property_values == nil, "bad argument #2 to new")
    
    local instance = Instance.new(class_name)
    if property_values then
        for property, value in pairs(property_values) do
            instance[property] = value
        end
    end
    
    return instance
end

-- Example:
local part = new("Part", {
    Name = "foo",
    CFrame = CFrame.new(0, 50, 0),
})
part.Parent = workspace

It’s still a good idea to avoid assigning the parent with this function, but it should be easier to assign properties.


#6

This is great! What if we edit it to set the Parent property last automatically? Like this:

local function new(class_name, property_values)
    assert(type(class_name) == "string", "bad argument #1 to new")
    assert(type(property_values) == "table" or property_values == nil, "bad argument #2 to new")
    
    local instance = Instance.new(class_name)
    if property_values then
        for property, value in pairs(property_values) do
            if property ~= "Parent" then
                instance[property] = value
            end
        end
    end
    
    if property_values.Parent ~= nil then
        instance.Parent = property_values.Parent
    end
    
    return instance
end

And this is a perfect use for this Lua syntax sugar:

local part = new "Part" {
    Parent = game.Workspace;
    Name = "foo2";
    CFrame = CFrame.new(0, -50, 0);
    Anchored = true;
    Locked = true;
}

#7

Sounds good! Unfortunately, the syntactic sugar does not support multiple arguments. We’ll have to use parenthesis in this case.


#8

You’re right. This will make the syntatic sugar work, it costs a bit of performance but is fun.

local function new(class_name)
    return function(property_values)
        assert(type(class_name) == "string", "bad argument #1 to new")
        assert(type(property_values) == "table" or property_values == nil, "bad argument #2 to new")

        local instance = Instance.new(class_name)

        if property_values then
            for property, value in pairs(property_values) do
                if property ~= "Parent" then
                    instance[property] = value
                end
            end
        end
        
        if property_values.Parent ~= nil then
            instance.Parent = property_values.Parent
        end
        
        return instance
    end
end

#9

I find myself using Validark’s “Make” function most of the time.