Understanding Mutation in JavaScript and How to Avoid It
In JavaScript, mutation happens when you change an object or an array directly. This can sometimes cause problems in your code, especially when you want to keep your data clean and easy to manage.
In this blog, I will explain:
1 What mutation is?
2. Why we should avoid it.
3. Easy ways to avoid mutation.
What is Mutation?
Mutation refers to the direct modification of an object, array, or other data structures in memory. When you mutate something, you are changing its original state.
In JavaScript, there are two types of data:
1. Primitive types: These include numbers, strings, and booleans. These cannot be changed (immutable).
let x = 10;
x = 20; // This creates a new value. The number 10 is not changed.
2. Objects and arrays: These can be changed (mutable).
let obj = { name: "John" };
obj.name = "Doe"; // This changes the original object.
let arr = [1, 2, 3];
arr.push(4); // This changes the original array.
You also pass objects and arrays by reference in functions. This means the function can change the original object or array.
function changeArray(array) {
array.push(4);
}
let numbers = [1, 2, 3];
changeArray(numbers); // Now, numbers = [1, 2, 3, 4]
Why Should We Avoid Mutation?
mutating objects or arrays can lead to bugs in your program.
For example:
- Unpredictable behavior: If two parts of your code share the same object, a change in one part can cause problems in the other part.
- Hard to debug: It’s difficult to find out where the change happened.
It is better to avoid mutation whenever possible to make your code cleaner and easier to understand.
How to Avoid Mutation?
1. Use const for Variables
Using const makes sure you don’t accidentally reassign a variable.
const obj = { name: "John" };
obj.name = "Doe"; // You can still change the object’s property.
// But you cannot reassign the object itself.
2. Create New Objects or Arrays
Instead of changing the original data, make a new one.
Recommended by LinkedIn
For Objects:
const obj = { name: "John", age: 25 };
const newObj = { ...obj, name: "Doe" }; // Creates a new object
console.log(newObj); // { name: "Doe", age: 25 }
For Arrays:
const arr = [1, 2, 3];
const newArr = [...arr, 4]; // Creates a new array
console.log(newArr); // [1, 2, 3, 4]
3. Use Non-Mutating Methods
Some methods in JavaScript change the original array (mutate), while others create a new one.
Avoid Mutating Methods:
let arr = [1, 2, 3];
arr.push(4); // Changes the original array
Use Non-Mutating Methods Instead:
const arr = [1, 2, 3];
const newArr = arr.concat(4); // Creates a new array
console.log(newArr); // [1, 2, 3, 4]
Other non-mutating methods include filter, map, and slice.
4. Clone Objects and Arrays
Use structuredClone (modern browsers) or a library like Lodash to copy deeply nested objects.
For Shallow Copies:
const obj = { name: "John" };
const clone = { ...obj }; // Makes a shallow copy
clone.name = "Doe";
console.log(obj.name); // "John" (unchanged)
For Deep Copies:
Use structuredClone (modern browsers) or a library like Lodash to copy deeply nested objects.
const nestedObj = { name: "John", details: { age: 25 } };
const deepClone = structuredClone(nestedObj);
deepClone.details.age = 30;
console.log(nestedObj.details.age); // 25 (unchanged)
5. Return New Data from Functions
Instead of changing the data you receive, return a new one.
function addItem(arr, item) {
return [...arr, item]; // Creates a new array
}
const numbers = [1, 2, 3];
const newNumbers = addItem(numbers, 4);
console.log(numbers); // [1, 2, 3] (unchanged)
console.log(newNumbers); // [1, 2, 3, 4]
Conclusion:
Avoiding mutation helps you write cleaner, safer, and easier-to-understand code. Remember these tips:
- Use const for variables.
- Create new objects or arrays instead of changing the original ones.
- Use non-mutating methods like concat and filter.
- Clone your data when needed.
- Always return new data from functions.
Following these steps will make your JavaScript code more predictable and less error-prone!