JavaScript Development Space

How to Create Immutable Objects in JavaScript

Mutable vs Immutable in JavaScript

In JavaScript, the terms mutable and immutable refer to whether a value or object can be changed after it is created. Understanding this distinction is important for writing predictable and bug-free code.

Mutable

  • Mutable objects can be changed or modified after they are created. This means that properties of the object can be added, removed, or altered at any point during the program’s execution.
  • Examples of mutable data types: objects, arrays, functions.

Example: Mutable Object

js
1 let person = { name: 'John', age: 30 };
2 person.age = 35; // Changing the 'age' property
3 console.log(person.age); // Output: 35
  • Here, the person object is mutable, meaning that its properties can be changed after its initial creation.

Example: Mutable Array

js
1 let numbers = [1, 2, 3];
2 numbers.push(4); // Adding a new element to the array
3 console.log(numbers); // Output: [1, 2, 3, 4]
  • Arrays are mutable, so you can modify them by adding or removing elements.

Immutable

  • Immutable objects, on the other hand, cannot be modified once they are created. Any operation that appears to modify an immutable object will instead return a new object or value, leaving the original unchanged.
  • Examples of immutable data types: primitive values like numbers, strings, booleans, and objects that have been explicitly made immutable.

Example: Immutable Primitive (String)

js
1 let name = 'John';
2 let newName = name.toUpperCase(); // Creates a new string with all uppercase letters
3 console.log(name); // Output: "John" (original string is unchanged)
4 console.log(newName); // Output: "JOHN" (new string)
  • Strings in JavaScript are immutable. Even though you can perform operations on them, the original string is never modified; instead, a new string is created.

Conclusion for Immutable

  • Mutable objects can be changed after creation, making them more flexible but also prone to accidental side effects.
  • Immutable objects, once created, cannot be changed, providing greater stability and reliability, especially in complex applications.

Understanding the difference between mutable and immutable data is essential for writing efficient, maintainable, and bug-free code in JavaScript.

Why Immutability Matters

  • Predictability: Immutable data makes it easier to reason about the state of an application. Since immutable objects don't change, you can be sure that their state remains constant over time.
  • No Side Effects: Immutability prevents unexpected side effects, as objects cannot be altered by other parts of the code.
  • Efficiency in Functional Programming: Immutability is a core concept in functional programming, as it allows for safer and more predictable operations without modifying the original data.

Creating Immutable Objects in JavaScript

Here are several ways to create immutable objects in JavaScript:

1. Using Object.freeze()

One of the most straightforward ways to make an object immutable is by using the built-in Object.freeze() method. It prevents modifications to the object, including adding, removing, or changing properties.

Example:

js
1 const person = {
2 name: 'John',
3 age: 30,
4 };
5
6 Object.freeze(person);
7
8 person.age = 35; // This will not change the age property
9 console.log(person.age); // Output: 30
  • Limitations: Object.freeze() only makes the first level of the object immutable. If the object contains nested objects, those nested objects will still be mutable.

2. Using Deep Freeze

To make an object and all of its nested properties immutable, you need to implement a deep freeze. This involves recursively freezing every property of the object.

Example:

js
1 function deepFreeze(obj) {
2 Object.keys(obj).forEach((prop) => {
3 if (typeof obj[prop] === 'object' && obj[prop] !== null) {
4 deepFreeze(obj[prop]);
5 }
6 });
7 return Object.freeze(obj);
8 }
9
10 const person = {
11 name: 'John',
12 address: {
13 city: 'New York',
14 zip: '10001',
15 },
16 };
17
18 deepFreeze(person);
19 person.address.city = 'Los Angeles'; // This will not change the city property
20 console.log(person.address.city); // Output: New York
  • Deep freeze ensures that nested objects are also immutable, providing true immutability across all object levels.

3. Using const for Primitive Values

Although const does not make objects immutable, it can be used to make primitive values (like strings, numbers, booleans) immutable by preventing reassignment.

Example:

js
1 const name = 'John';
2 name = 'Jane'; // This will throw an error because 'name' is a constant
  • Note: const only ensures that the variable reference cannot be changed, but it does not freeze the object itself.

4. Using Immutable.js

For more complex immutability needs, you can use libraries like Immutable.js, which provides data structures that are immutable by default. It supports immutable lists, maps, sets, and other types, making it easy to work with immutable data.

Example:

js
1 const { Map } = require('immutable');
2
3 const person = Map({ name: 'John', age: 30 });
4
5 const updatedPerson = person.set('age', 35);
6 console.log(person.get('age')); // Output: 30 (original remains unchanged)
7 console.log(updatedPerson.get('age')); // Output: 35 (new object with updated value)
  • Immutable.js is a good solution when working with large applications that require efficient handling of immutable data.

5. Using Object.defineProperty()

You can also use Object.defineProperty() to create immutable properties on an object by setting writable: false. This will prevent the property from being changed, but will not make the entire object immutable.

Example:

js
1 const person = {};
2 Object.defineProperty(person, 'name', {
3 value: 'John',
4 writable: false,
5 });
6
7 person.name = 'Jane'; // This will not change the name property
8 console.log(person.name); // Output: John
  • Note: This method only works for specific properties and needs to be applied property-by-property.

6. Using Spread Operator to Create Immutable Copies

Another method to ensure immutability is to create a new object every time you want to "change" it. Instead of modifying the original object, you can use the spread operator (...) to create a new copy of the object.

Example:

js
1 const person = { name: 'John', age: 30 };
2
3 // Create an immutable copy with an updated age
4 const updatedPerson = { ...person, age: 35 };
5
6 console.log(person.age); // Output: 30 (original remains unchanged)
7 console.log(updatedPerson.age); // Output: 35 (new copy with updated value)
  • This approach is commonly used in modern JavaScript frameworks (like React) where immutability is often enforced.

Conclusion

Creating immutable objects in JavaScript can be achieved using various methods, from simple freezing with Object.freeze() to more complex solutions like deep freezing or using libraries like Immutable.js. Immutability ensures data integrity by preventing unintended changes to objects, which can help reduce bugs and improve code maintainability.

JavaScript Development Space

© 2024 JavaScript Development Space - Master JS and NodeJS. All rights reserved.