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

Thanks…

I also dissect and speculate on design and development.
Digging into subtle details and implications, and exploring broad perspectives and potential paradigm shifts.
Check out my conceptual articles on Substack or find my latest below.


You can also find me on Threads, Bluesky, Mastodon, or X for more diverse posts about ongoing projects.

My latest articles

Designing immersive experiences

In traditional cinema, TV, or even the more modern phone screen, there’s limited screen real-estate. But removing that limitation creates a design problem…

The future is not prompt engineered

Let’s not pretend the importance of prompt engineering is ubiquitous. The most prevalent power of generative AI is in the way it adapts to us, not the other way around…

The typography of dates, times, & filenames

A deep dive into carefully considered date formatting, line length and general typography attributes of filenames…

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.