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.

slice(start, end) creates a shallow copy

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

Negative indexes in slice

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(start, deleteCount, …items) mutates

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"]

Inserting or deleting with splice

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]

toSpliced() avoids in-place mutation

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]

Shallow copying does not clone nested objects

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

Choose the method by intent

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"]

Handle ranges, performance and compatibility

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

Conclusion

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.

  • img