Mutable and Immutable in JavaScript

Mutable and Immutable in JavaScript

This article explains mutable and immutable concepts in JavaScript.

YouTube Video

Mutable and Immutable in JavaScript

What is Mutable?

Mutable means that a value can be modified. Objects and arrays, which are reference types, are typical examples of mutable data structures.

Example of a Mutable Object

1let person = { name: "Alice", age: 25 };
2person.age = 26;
3console.log(person); // { name: "Alice", age: 26 }

In this code, the age property of the person object is changed from 25 to 26. Since objects are passed by reference, the content at the memory address stored in the person variable is modified.

Example of a Mutable Array

1let numbers = [1, 2, 3];
2numbers.push(4);
3console.log(numbers); // [1, 2, 3, 4]

In this code, the push method is used to add a new element 4 to the original array. This modifies the original array, making it a mutable operation.

Example in a Function

 1// Function to append a value to an array
 2function append(arr, value) {
 3    arr.push(value); // Modify the original array
 4    console.log(arr);
 5}
 6
 7let numbers = [1, 2, 3];
 8append(numbers, 4);
 9
10console.log(numbers); // [1, 2, 3, 4] (original array is modified)

When performing mutable operations inside a function, the original array is also modified.

What is Immutable?

Immutable means that a value cannot be modified. Primitive types are fundamentally immutable.

Example of an Immutable Primitive Type

1let str = "hello";
2str[0] = "H";
3console.log(str); // "hello"

Attempting to change the first character of the string str to H fails because strings are immutable.

Example in a Function

 1// Function to increment a number
 2function increment(num) {
 3    num++; // This modifies only the local copy of num
 4    console.log(num);
 5}
 6
 7let number = 10;
 8increment(number);
 9
10console.log(number); // 10 (original number remains unchanged)

Since numbers are immutable, operations inside a function do not affect the original variable.

Immutable Operations on Arrays

Arrays are mutable, but creating a new array instead of modifying the original one allows for immutable operations.

1// Create an array of numbers
2let numbers = [1, 2, 3];
3
4// Create a new array by spreading the original and adding a new element
5let newNumbers = [...numbers, 4];
6
7console.log(numbers); // [1, 2, 3] (original array is unchanged)
8console.log(newNumbers); // [1, 2, 3, 4] (new array with an added element)

Here, the spread syntax (...) is used to create a new array newNumbers. Since the original numbers array is not modified, this is an immutable operation.

Benefits of Using Immutable Data Structures

Improved Predictability

Since immutable data cannot be changed, unexpected modifications are less likely, reducing the risk of bugs.

Compatibility with Libraries Based on Immutability

Libraries like React and Redux are often designed with immutable data in mind, making state management easier when used correctly.

Making Objects Immutable with Object.freeze

Object.freeze can be used to prevent modifications to an object.

1// Create a frozen object (properties cannot be modified)
2const person = Object.freeze({ name: "Alice", age: 25 });
3
4// Attempt to modify a property (ignored in non-strict mode, error in strict mode)
5person.age = 26;
6
7console.log(person); // { name: "Alice", age: 25 }

However, Object.freeze performs a shallow freeze, meaning that properties of nested objects remain mutable.

1// Create a frozen object with a nested object
2const user = Object.freeze({ profile: { name: "Bob" } });
3
4// Attempt to modify a nested property (this works because Object.freeze() is shallow)
5user.profile.name = "Charlie";
6
7console.log(user.profile.name); // "Charlie" (nested object is still mutable)

To create a fully immutable object, a deep freeze is required.

 1// Function to deeply freeze an object, making all nested objects immutable
 2function deepFreeze(obj) {
 3  Object.keys(obj).forEach(key => {
 4    if (typeof obj[key] === "object" && obj[key] !== null) {
 5      deepFreeze(obj[key]); // Recursively freeze nested objects
 6    }
 7  });
 8  return Object.freeze(obj); // Freeze the top-level object
 9}
10
11// Create a deeply frozen object
12const user = deepFreeze({ profile: { name: "Bob" } });
13
14// Attempt to modify a nested property (ignored)
15user.profile.name = "Charlie";
16
17console.log(user.profile.name); // "Bob" (unchanged due to deep freeze)

Summary

  • Mutable data can be modified, including objects and arrays.
  • Immutable data cannot be modified, including primitive types such as strings and numbers.
  • Using spread syntax or map enables immutable data operations..
  • Object.freeze and deepFreeze can be used to prevent modifications to objects.
  • Using immutable data allows for more predictable and less error-prone code.

Immutable design enhances code safety and readability, so make good use of it!

You can follow along with the above article using Visual Studio Code on our YouTube channel. Please also check out the YouTube channel.

YouTube Video