In JavaScript, what is the difference between Array.prototype.slice() and Array.prototype.splice(), and what does each one return?
answer
- one letter apart, opposite behaviour
- one copies a range, one edits in place
- check what each hands back
- splice returns what was removed
- slice(0,3) reads, splice(0,3) removes
basics
~20 sslice() copies: it leaves the original array untouched and returns a new array holding the selected range. splice() edits in place: it removes and/or inserts elements in the original array and returns an array of the elements it removed.
solid answer
~40 s`slice(start, end)` is a copying method. It returns a new array with the elements from `start` up to but not including `end`, supports negative indexes counted from the end, and never touches the receiver — `arr.slice()` with no arguments is the classic full shallow copy idiom. `splice(start, deleteCount, ...items)` is a mutating method: it deletes `deleteCount` elements at `start`, inserts any extra arguments there, changes `length` and reindexes the array — and its return value is the array of *removed* elements, not the array you spliced. So `list.slice(0, 3)` hands you the first three items and leaves `list` alone, while `list.splice(0, 3)` hands you the same three items but leaves `list` three elements shorter. Confusing the two is one of the most common accidental-mutation bugs in JavaScript.
code
javascript · 10 linesconst list = ['a', 'b', 'c', 'd'];
const copied = list.slice(1, 3);
console.log(copied); // ['b', 'c']
console.log(list.length); // 4 — original intact
const removed = list.splice(1, 2, 'X');
console.log(removed); // ['b', 'c'] — what came out
console.log(list); // ['a', 'X', 'd'] — mutated
console.log(list.length); // 3go deeper
Be ready to state plainly that slice copies and splice mutates, and to say what each returns: a new array of the selected range versus the elements that were removed.
Explain the argument shapes — slice takes start and end while splice takes start and deleteCount plus insertions — and demonstrate the insert idiom splice(i, 0, x) and negative-index handling.
Show the judgment of not mutating arrays you do not own: copy at function boundaries, treat splice's return value as "what came out", and name toSpliced as the copying counterpart.
Frame it as an API-contract decision: whether your utilities take ownership of arguments or promise not to mutate them, and how you make that contract obvious in naming, documentation and review.
## Two similar names, opposite behaviour JavaScript arrays are mutable objects, and the array methods fall into two families: **mutating** methods that change the array they are called on, and **copying** methods that leave it alone and hand back a new array. `slice` and `splice` sit on opposite sides of that line while differing by one letter, which is exactly why interviewers pair them. ## slice(start, end) — copying `slice` returns a **new** array containing the elements from index `start` up to but *not including* `end`. Both arguments are optional: `start` defaults to `0`, `end` defaults to `length`. Negative values count back from the end of the array. ```js const a = ['a', 'b', 'c', 'd']; console.log(a.slice(1, 3)); // ['b', 'c'] console.log(a.slice(-2)); // ['c', 'd'] console.log(a); // ['a','b','c','d'] — untouched const full = a.slice(); // full shallow copy ``` The copy is **shallow**: the new array holds the same element values, so for object elements both arrays point at the same objects. ## splice(start, deleteCount, ...items) — mutating `splice` edits the receiver in place. It removes `deleteCount` elements beginning at `start` and inserts any further arguments at that position, shifting the remaining elements and updating `length`. Its **return value is an array of the removed elements**. ```js const a = ['a', 'b', 'c', 'd']; const removed = a.splice(1, 2, 'X'); console.log(removed); // ['b', 'c'] <- what came out console.log(a); // ['a', 'X', 'd'] <- the array itself changed a.splice(1, 0, 'y'); // deleteCount 0 = pure insert console.log(a); // ['a', 'y', 'X', 'd'] a.splice(2); // omitting deleteCount removes to the end console.log(a); // ['a', 'y'] ``` A negative `start` counts from the end, and a `start` past `length` clamps to the end, so `arr.splice(arr.length, 0, x)` is an awkward `push`. ## Where the confusion bites The classic bug is reaching for `splice` when you only wanted to *read* a range: ```js function topThree(list) { return list.splice(0, 3); // BUG: shrinks the caller's list } ``` The function returns the right three items, so a quick test passes — but every call permanently removes three elements from the array that was passed in. `list.slice(0, 3)` is the correct call. The mirror-image mistake is expecting `splice` to hand back the *edited* array, which leads to `const updated = arr.splice(1, 1)` and then wondering why `updated` has one element in it. ## Return-value cheat sheet for the neighbours It helps to memorise the whole in-place family, because their return values are all different: `push`/`unshift` return the new `length`, `pop`/`shift` return the single removed element, `splice` returns an array of removed elements, and `reverse`/`sort`/`fill`/`copyWithin` return **the same array object** they just mutated. Meanwhile `slice`, `concat`, `map`, `filter`, `flat` and `flatMap` all return fresh arrays. ## The ES2023 copying counterpart ES2023 added `toSpliced(start, skipCount, ...items)`, the non-mutating twin of `splice`. Note the return value flips: `toSpliced` returns the **resulting** array and throws the removed elements away, whereas `splice` returns the removed elements and keeps the result in place. ```js const a = ['a', 'b', 'c', 'd']; const b = a.toSpliced(1, 2, 'X'); console.log(b); // ['a', 'X', 'd'] console.log(a); // ['a','b','c','d'] — untouched ``` ## Practical guidance If the array is shared — a function parameter, a value stored in a container someone else reads, anything you did not create in the current function — prefer the copying method and hand back a new array. Reach for `splice` when you deliberately own the array and want an in-place edit, and read its return value as "what came out", never as "the new array".
- What does splice return if deleteCount is 0?An empty array. With `deleteCount` of `0`, `splice` removes nothing and only inserts the extra arguments, so the "removed elements" array it returns has no members — but the receiver is still mutated by the insertion. It is the standard idiom for inserting at an index: `arr.splice(2, 0, 'x')`.
- How would you delete one element from an array without mutating it?Copy around the index: `arr.slice(0, i).concat(arr.slice(i + 1))`, or in an ES2023 runtime simply `arr.toSpliced(i, 1)`, which returns the resulting array and leaves the original alone. Avoid `delete arr[i]` — that leaves a hole and does not change `length`.
- Is the array returned by slice() safe to mutate freely?The array object itself is new, so pushing, sorting or reversing it will not affect the original. But the copy is shallow: the elements are the same references, so mutating a nested object such as `copy[0].name = 'x'` is visible through the original array too.
saying these in an interview costs you the question
- Thinking splice returns the modified array
- Saying slice mutates because it changes indexes
- Using splice(0, 3) to peek at the first three items
- Believing both methods take (start, end) arguments
- Assuming the array from slice() deep-copies nested objects