Slice vs Splice Explained: When to Use Each in JavaScript [2025]
In JavaScript, Array.prototype.slice() copies a selected range of elements into a new array without changing the original array. Array.prototype.splice() changes the original array by deleting, inserting or replacing elements, and returns the removed items. The similar names conceal an important mutation boundary.
For user-interface state and functional transformations, use nonmutating approaches unless in-place change is intentional. Modern JavaScript also offers toSpliced(), which returns a changed copy without modifying the source. Check runtime compatibility when targeting older browsers or Node.js releases.
The start index is included and the end index excluded. If end is omitted, copying continues to the array’s end. slice() does not modify the original array, but copied objects remain shared references.
const source = [10, 20, 30, 40];
const copy = source.slice(1, 3);
console.log(copy); // [20, 30]
console.log(source); // unchanged
A negative index counts backward from the array’s end. This is useful for getting the last few elements, but very large negative indexes are effectively clamped. A zero-length range returns an empty array.
const values = ["a", "b", "c", "d"];
console.log(values.slice(-2)); // ["c", "d"]
console.log(values.slice(2, 2)); // []
splice removes up to deleteCount items starting at the specified position and optionally inserts new elements there. It returns an array of removed elements. A splice operation changes indexes of later elements in the source array.
const values = ["a", "b", "c"];
const removed = values.splice(1, 1, "x");
console.log(removed); // ["b"]
console.log(values); // ["a", "x", "c"]
Setting deleteCount to zero inserts elements. Omitting extra items removes elements. Explicitly pass the desired count rather than assume the second argument is an end index like slice().
const q = [1, 3];
q.splice(1, 0, 2); // [1, 2, 3]
q.splice(0, 1); // [2, 3]
ES2023 introduced toSpliced(), which behaves like splice’s change operation but returns a new array. The original array is unchanged. It is useful for immutable state updates, provided the deployment runtime supports the method.
const old = [1, 2, 3];
const next = old.toSpliced(1, 1, 9);
console.log(next); // [1, 9, 3]
console.log(old); // [1, 2, 3]
slice() and toSpliced() return new arrays, but nested objects are generally shared references. Mutating an object from a copied array can still affect the original. Copy nested records as needed when state isolation is important.
const a = [{count: 1}];
const b = a.slice();
b[0].count = 2;
console.log(a[0].count); // 2
Use slice for extraction, splice when changing an owned array in place, and toSpliced or a spread-based expression when a new array is needed. In React or similar state models, avoid direct mutation when updates rely on immutable identity.
const old = ["a", "b", "c"];
const next = [...old.slice(0, 1), ...old.slice(2)];
console.log(next); // ["a", "c"]
Indexes outside array boundaries are normalized; negative start positions have their documented meaning. Copying a large array costs memory, while splice may shift elements. Benchmark actual workloads rather than claim one method is universally faster.
const data = [5, 10, 15];
console.log(data.slice(99)); // []
const version = typeof [].toSpliced;
console.log(version); // "function" on supported runtimes
slice copies a range and leaves the source array alone; splice edits the original and returns removed items. toSpliced supports nonmutating edits on compatible runtimes. Before changing code, decide whether mutation is part of the intended contract.
