Spread can be a timebomb

I have, a few times in the passed, used the spread syntax (…) in JavaScript in a way that has later caused a very hard-to-debug error in my code. Essentially, I’ve used the syntax on a single level array or object only to later increase it’s depth—While spread remains a shallow copy.

This article isn’t to teach you how to use spread, so I’ll keep this part light. Below are a few examples of using spread.

Let array1 = [1, 2, 3];
Let array2 = […array1];

Let object1 = {
	property1: true,
	property2: "red"
};
let object2 = {…object1};

let standardMeal = {
	healthy: false,
	tasty: true,
	servings: 1
}
let breakfast = {
	…standardMeal,
	ingredients = ["oats", "muesli", "milk"]
}

In each case, the spread operator (…) spreads it’s properties into the new object or array. So…

// array2 becomes:
[1, 2, 3]

// object2 becomes:
{
	property1: true,
	property2: "red"
};

// breakfast becomes
{
	healthy: false,
	tasty: true,
	servings: 1,
	ingredients = ["oats", "muesli", "milk"]
}

The problem

All of the properties that we’ve spread so far, however, are primitive values (strings, numbers, booleans), so their values are duplicated as new properties. This means if we change array1, it won’t affect array2. Likewise for object1 or standardMeal. The values were copied and no long maintain a link.

The problem arises, when one of the parameters being spread is not a primitive. In this instance, a reference to that object variable is copied into the new object.

For instance, if we were to do the same thing with the breakfast variable, we would get something conceptually similar to this.

// What we did
let breakfast = {
	healthy: false,
	tasty: true,
	servings: 1,
	ingredients = ["oats", "muesli", "milk"]
}

Let tuesdayBreakfast = {
	 …breakfast,
}

// What we’d get in Tuesday breakfast
{
	healthy: false,
	tasty: true,
	servings: 1,
	ingredients = <reference-to-ingredients-in-breakfast>
}

If we accessed tuesdayBreakfast’s ingredients, we would still see the ingredients as usual. It would appear successful. But what we actually have is not an original duplicate of the data, but a reference to the original data.

This Means if we change the ingredients in the breakfast variable, it would also affect the tuesdayBreakfast variable and vice versa.

Note that this is the same way non-primitive variables work in general. If I define an object and then set another variable to point at it. That’s what it does. It points at it (or creates a reference to it).

Let firstObj = {
	Name: ‘Dale’,
	Height: ‘182cm’
}
Let secondObj = firstObj;

// What we get 
secondObj = <reference-to-firstObj>

Spread is a slippery slope

I absolutely love the spread syntax. The purpose of this article isn’t to dissuade you from using it. Rather, it’s important to know where it might break so you don’t go chasing ghost bugs in your program.

Because of this, if I know that I want to create a complete duplicate of an object, even if that object is only a single level object or array, I now always use JSON.parse(JSON.stringify(object)).

This might feel a little overkill and harder to read, but it means that even if the original object grows in complexity, I know that the setup won’t break and create confusing errors.

Is this less efficient? Maybe, I don’t know. But optimising can (and should), be done later when your program settles close to it’s more permanent setup—So you can come back to it then if necessary.

I also discuss JSON.parse in my article Deep Cloning Efficiently if you want to understand it better.

That’s it

I’d love to know if any of these articles helped you.
Share what you’re building or ask me a question on Threads or somewhere else.

Instagram is a great place to see my final creative coding experiments, while the others are are great place to connect or see progress updates.

If my content has helped you, I’d never say no to donations to help me keep writing.

Here are some other things you might like


Author:

Date:

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.