In JavaScript, arrays are mutable, meaning their elements can be changed without creating a new array. However, there are ways to work with arrays immutably, ensuring the original array is not modified. Here's an explanation of both mutable and immutable operations in JavaScript arrays:
Mutable Operations
When you perform mutable operations on an array, you directly modify the array's contents. This can lead to unexpected side effects and make it challenging to reason about the code. Here are some examples of mutable operations:
push(), pop(), shift(), unshift(), splice(), reverse(), sort(), fill() ...
Immutable Operations
In contrast, immutable operations do not modify the original array. Instead, they return a new array with the desired changes, leaving the original array unchanged. This approach is safer and helps prevent bugs caused by unintended side effects. Here are some examples of immutable operations:
concat(), slice(), map(), filter(), reduce(), toSorted(), toReversed(), toSpliced() ...
push vs concat
let numbers = [1, 2, 3, 4, 5];
// Mutable operation: Modifies the original array
numbers.push(6);
console.log(numbers); // Output: [1, 2, 3, 4, 5, 6]
let numbers = [1, 2, 3, 4, 5];
// Immutable operation: Returns a new array without modifying the original
let newNumbers = numbers.concat(6);
console.log(numbers); // Output: [1, 2, 3, 4, 5]
console.log(newNumbers); // Output: [1, 2, 3, 4, 5, 6]
splice vs toSpliced
let numbers = [1, 2, 3, 4, 5];
// Mutable operation: Modifies the original array
numbers.splice(2, 1);
console.log(numbers); // Output: [1, 2, 4, 5]
let numbers = [1, 2, 3, 4, 5];
// Immutable operation: Returns a new array without modifying the original
let splicedNumbers = numbers.toSpliced(2, 1);
console.log(numbers); // Output: [1, 2, 3, 4, 5]
console.log(splicedNumbers); // Output: [1, 2, 4, 5]
sort vs toSorted
let numbers = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5];
// Mutable operation: Modifies the original array
numbers.sort((a, b) => a - b);
console.log(numbers); // Output: [1, 1, 2, 3, 3, 4, 5, 5, 5, 6, 9]
let numbers = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5];
// Immutable operation: Returns a new array without modifying the original
let sortedNumbers = numbers.toSorted();
console.log(numbers); // Output: [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]
console.log(sortedNumbers); // Output: [1, 1, 2, 3, 3, 4, 5, 5, 5, 6, 9]
reverse vs toReversed
let numbers = [1, 2, 3, 4, 5];
// Mutable operation: Modifies the original array
numbers.reverse();
console.log(numbers); // Output: [5, 4, 3, 2, 1]
let numbers = [1, 2, 3, 4, 5];
// Immutable operation: Returns a new array without modifying the original
let reversedNumbers = numbers.toReversed();
console.log(numbers); // Output: [1, 2, 3, 4, 5]
console.log(reversedNumbers); // Output: [5, 4, 3, 2, 1]
Conclusion
By understanding the concepts of mutability and immutability in JavaScript arrays, you can write more efficient and bug-free code. When possible, prefer immutable operations to avoid unintended side effects and make your code easier to reason about.