JavaScript: Array Methods (Mutating vs Non-Mutating)
Arrays are the most utilized data structure in frontend development. In modern frameworks like React, mutating state directly is a cardinal sin. Therefore, interviewers frequently test your knowledge on which array methods mutate the original array, and which ones return a new copy.
1. Mutating Methods (Modifies the Original)
These methods permanently alter the array they are called on. When working with state in UI frameworks, you should generally avoid these unless you are operating on a cloned array.
push() and pop() (End of Array)
push(val): Adds an element to the end of the array. Returns the new length.pop(): Removes the last element. Returns the removed element.
unshift() and shift() (Beginning of Array)
unshift(val): Adds an element to the beginning. Returns the new length. (Performance heavy on large arrays as it shifts all indexes).shift(): Removes the first element. Returns the removed element.
splice(startIndex, deleteCount, ...itemsToAdd)
The ultimate Swiss Army knife for arrays. It modifies the array in place by removing, replacing, or adding elements.
const arr = ["a", "b", "c", "d"];
arr.splice(1, 2, "X"); // Starts at index 1, removes 2 items, inserts "X"
console.log(arr); // ["a", "X", "d"]reverse() and sort()
Both of these methods mutate the original array.
const nums = [3, 1, 2];
nums.sort(); // Mutates 'nums' to [1, 2, 3]2. Non-Mutating Methods (Returns a New Array/Value)
These methods are purely functional. They do not touch the original array; instead, they generate and return a brand-new array (or a specific value).
slice(startIndex, endIndex)
Returns a shallow copy of a portion of an array.
const arr = ["a", "b", "c", "d"];
const copy = arr.slice(1, 3); // Extracts from index 1 up to (but not including) index 3
console.log(copy); // ["b", "c"]
console.log(arr); // ["a", "b", "c", "d"] (Unchanged!)concat()
Merges two or more arrays into a new array.
const arr1 = [1, 2];
const arr2 = [3, 4];
const merged = arr1.concat(arr2); // [1, 2, 3, 4]ES6 Iterators: map(), filter(), reduce()
The holy trinity of non-mutating data transformation. They always iterate over the original array and return a completely new array (or accumulated value).
3. The splice vs slice Confusion
This is a favorite rapid-fire interview question.
| Feature | splice() | slice() |
|---|---|---|
| Mutates original? | ✅ Yes | ❌ No (Returns a copy) |
| Parameters | (start, deleteCount, items...) | (start, end) |
| Use case | Deleting/Inserting items in place | Extracting a subset of an array safely |
4. Modern Non-Mutating Alternatives (ES2023)
JavaScript recently introduced non-mutating versions of classic mutating methods:
toReversed()(Non-mutatingreverse)toSorted()(Non-mutatingsort)toSpliced()(Non-mutatingsplice)with(index, value)(Non-mutating assignment, instead ofarr[index] = value)
const original = [3, 1, 2];
const sorted = original.toSorted();
console.log(original); // [3, 1, 2] (Preserved!)
console.log(sorted); // [1, 2, 3]Senior-Level Interview Answer
A critical concept in JavaScript array manipulation is distinguishing between mutating and non-mutating methods. Mutating methods, such as
push,splice, andsort, modify the data structure in place. This is memory efficient but heavily discouraged in declarative state-management paradigms like React or Redux, as it circumvents reference-equality checks. Non-mutating methods, likeslice,concat,map, and the ES2023 additions liketoSorted(), execute immutably by returning entirely new array instances. To write bug-free UI code, developers should default to non-mutating methods, or ensure they clone the array (using spread syntax) before applying mutating operations.
Common Interview Mistakes
❌ Misunderstanding sort() string coercion
If you call sort() on an array of numbers without a callback function, it sorts them alphabetically by converting them to strings, resulting in bizarre bugs.
const nums = [10, 2, 30];
nums.sort();
console.log(nums); // [10, 2, 30] (Because "10" comes before "2" alphabetically!)
// Correct way:
nums.sort((a, b) => a - b);