Understanding Mutable and Immutable Data Types in JavaScript

When we first start learning JavaScript, we come across terms like mutable, immutable, and reference types—which can sound a bit intimidating. My goal in this article is to explain these concepts in plain English to help solidify my understanding while also making them easy for others to grasp!

What Does Mutable and Immutable Mean?

In simple terms:

  • Mutable means changeable—you can modify the original value.
  • Immutable the opposite of mutable means unchangeable—once created, the value cannot be changed.

Before we jump into code examples, it is important to note that all primitive data types in JavaScript are immutable. These are Strings, Numbers, BigInt, Null, Undefined, and Symbol. Although we can reassign primitive values, we cannot change their actual content in memory. It is important to note that reassigning is not the same as changing a value. Let's explore some examples:

let someString = 'string';
console.log(someString); // 'string'

someString = 'changed string';
console.log(someString); // 'changed string'

At first glance, it looks like we changed "string" to "changed string", but that’s not actually the case! What happens under the hood is:

  • JavaScript creates a new string ("changed string") in memory.
  • The variable someString now points to the new value.
  • The original string ("string") remains unchanged and is eventually removed by JavaScript's garbage collector.

Proof That Primitive Types Are Immutable

let someString = "hello";
someString[0] = "H"; // Attempting to modify a character
console.log(someString); // "hello" (unchanged)

Even though we tried to modify the first letter of the string, nothing changed! This proves that primitive values cannot be modified in place. If we want to change a string, we must create a new one using methods like .replace(), .toUpperCase(), etc.:

let newString = someString.replace("h", "H");
console.log(newString); // "Hello"

Here, .replace() does not modify someString; it simply returns a new string.

Objects and Arrays Are Mutable (Reference Types)

For a long time, I kept hearing "Objects and arrays are reference types", but I didn’t fully understand what that meant. Here’s a simple analogy that helped me:

  • Imagine your variable as a label that points to an address in memory.
  • The actual object or array is the house at that address.
  • If two variables point to the same address, changing one affects the other—because both are looking at the same house!
  • When you assign an object or array to another variable, JavaScript does not create a copy—instead, both variables point to the same address in memory.
let myArray = [1, 2, 3, 4, 5];
let yourArray = myArray; // Both now point to the same array in memory

yourArray.pop();
console.log("myArray:", myArray); // [1, 2, 3, 4]
console.log("yourArray:", yourArray); // [1, 2, 3, 4]

myArray.push(9);
console.log("myArray:", myArray); // [1, 2, 3, 4, 9]
console.log("yourArray:", yourArray); // [1, 2, 3, 4, 9]

Since both variables point to the same array, changes made to yourArray also affect myArray.

Why Is This a Problem?

If myArray and yourArray are used in different parts of your program, and changes are made directly to either one of the variables, this can cause unexpected and hard-to-debug issues. This is one of the reasons making a copy is a safe and best practice.

How to Avoid Unwanted Mutations

Instead of modifying an array or object directly, we should create a copy using the spread operator (...):

let myArray = [1, 2, 3, 4, 5];
let yourArray = [...myArray]; // Creates a NEW array

yourArray.pop();
console.log("myArray:", myArray); // [1, 2, 3, 4, 5] (unchanged)
console.log("yourArray:", yourArray); // [1, 2, 3, 4] (changed)

Now, modifying yourArray does not affect myArray, because they are separate copies. Similarly, for objects:

let myObject = { name: "Susu", age: 25 };
let yourObject = { ...myObject }; // Creates a new object

yourObject.age = 30;
console.log("myObject:", myObject); // { name: "Susu", age: 25 }
console.log("yourObject:", yourObject); // { name: "Susu", age: 30 }

Using { ...myObject } ensures that myObject remains unchanged.

Final Takeaways

  • Primitive types (strings, numbers, etc.) are immutable—you can reassign them but not modify their value directly.
  • Objects and arrays are mutable (reference types)—assigning them to a new variable does not create a copy, it just creates a new reference.
  • To avoid unintended mutations, use the spread operator (...) to create copies of arrays and objects.

Understanding these concepts will help you write cleaner, more predictable JavaScript code. 🎯🚀

Would love to hear if this explanation makes sense! Let me know in the comments! 😊

Avatar for Fatima Aminu

Written by Fatima Aminu

Loading

Fetching comments

Hey! 👋

Got something to say?

or to leave a comment.