JavaScript Splice, Slice, and Essential Array Methods
February 5, 2026 • 9 min read
Think of an array like a train with numbered cars. Some operations let you peek at specific cars without changing anything (slice). Others let you add, remove, or swap cars while the train is running (splice). Understanding which methods modify your array and which create copies is crucial to avoiding bugs.
Let’s demystify JavaScript’s most commonly confused array methods.
The Slice vs Splice Confusion
These two methods have similar names but completely different behaviors. This is the most common source of array-related bugs.
slice - Creates a copy (non-destructive):
const fruits = ["apple", "banana", "cherry", "date", "elderberry"];
// slice(start, end) - end is NOT included
const middle = fruits.slice(1, 4);
console.log(middle); // ["banana", "cherry", "date"]
console.log(fruits); // ["apple", "banana", "cherry", "date", "elderberry"]
// Original unchanged!
splice - Modifies the original (destructive):
const fruits = ["apple", "banana", "cherry", "date", "elderberry"];
// splice(start, deleteCount, ...itemsToAdd)
const removed = fruits.splice(1, 2, "blueberry");
console.log(removed); // ["banana", "cherry"] - what was removed
console.log(fruits); // ["apple", "blueberry", "date", "elderberry"]
// Original modified!
Memory trick: Slice sounds like “slicing” a cake - you get a piece but the cake is still there. Splice sounds like “splicing” a film reel - you’re physically cutting and joining tape.
Mastering slice
The slice method extracts a portion of an array into a new array. The original stays intact.
const numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
// Basic slicing
console.log(numbers.slice(2, 5)); // [2, 3, 4]
console.log(numbers.slice(5)); // [5, 6, 7, 8, 9] - from index 5 to end
console.log(numbers.slice()); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - full copy
// Negative indices count from end
console.log(numbers.slice(-3)); // [7, 8, 9] - last 3 elements
console.log(numbers.slice(-5, -2)); // [5, 6, 7] - from 5th-last to 2nd-last
console.log(numbers.slice(2, -2)); // [2, 3, 4, 5, 6, 7] - mix positive and negative
Common slice patterns:
const items = ["a", "b", "c", "d", "e"];
// Get first N elements
const firstThree = items.slice(0, 3); // ["a", "b", "c"]
// Get last N elements
const lastTwo = items.slice(-2); // ["d", "e"]
// Remove first element (immutably)
const withoutFirst = items.slice(1); // ["b", "c", "d", "e"]
// Remove last element (immutably)
const withoutLast = items.slice(0, -1); // ["a", "b", "c", "d"]
// Clone an array
const clone = items.slice(); // ["a", "b", "c", "d", "e"]
Mastering splice
The splice method changes an array by removing, replacing, or adding elements. It modifies the original and returns the removed elements.
// splice(startIndex, deleteCount, item1, item2, ...)
const colors = ["red", "green", "blue", "yellow", "purple"];
// Remove 2 elements starting at index 1
const removed = colors.splice(1, 2);
console.log(removed); // ["green", "blue"]
console.log(colors); // ["red", "yellow", "purple"]
Insert without removing (set deleteCount to 0):
const letters = ["a", "b", "e", "f"];
// Insert at index 2, remove 0 elements
letters.splice(2, 0, "c", "d");
console.log(letters); // ["a", "b", "c", "d", "e", "f"]
Replace elements:
const scores = [85, 90, 78, 92, 88];
// Replace 2 elements starting at index 1
scores.splice(1, 2, 95, 100);
console.log(scores); // [85, 95, 100, 92, 88]
Remove single element by index:
const tasks = ["email", "meeting", "lunch", "code", "review"];
// Remove element at index 2
tasks.splice(2, 1);
console.log(tasks); // ["email", "meeting", "code", "review"]
Adding and Removing Elements
JavaScript provides specialized methods for working with the ends of arrays.
push and pop - Work with the end (like a stack):
const stack = ["first", "second"];
// push: add to end, returns new length
const newLength = stack.push("third");
console.log(stack); // ["first", "second", "third"]
console.log(newLength); // 3
// pop: remove from end, returns removed element
const last = stack.pop();
console.log(stack); // ["first", "second"]
console.log(last); // "third"
// Push multiple at once
stack.push("a", "b", "c");
console.log(stack); // ["first", "second", "a", "b", "c"]
unshift and shift - Work with the beginning (like a queue):
const queue = ["second", "third"];
// unshift: add to beginning, returns new length
queue.unshift("first");
console.log(queue); // ["first", "second", "third"]
// shift: remove from beginning, returns removed element
const first = queue.shift();
console.log(queue); // ["second", "third"]
console.log(first); // "first"
Performance note: push/pop are faster than unshift/shift because adding or removing from the beginning requires re-indexing all elements.
Searching and Checking
indexOf and lastIndexOf - Find position:
const letters = ["a", "b", "c", "b", "a"];
// indexOf: first occurrence
console.log(letters.indexOf("b")); // 1
console.log(letters.indexOf("z")); // -1 (not found)
console.log(letters.indexOf("b", 2)); // 3 (search from index 2)
// lastIndexOf: last occurrence
console.log(letters.lastIndexOf("b")); // 3
console.log(letters.lastIndexOf("a")); // 4
includes - Check existence:
const fruits = ["apple", "banana", "cherry"];
console.log(fruits.includes("banana")); // true
console.log(fruits.includes("grape")); // false
// More readable than indexOf !== -1
if (fruits.includes("apple")) {
console.log("We have apples!");
}
at - Access by index (ES2022):
const items = ["first", "second", "third", "fourth", "last"];
// Positive index (same as bracket notation)
console.log(items.at(0)); // "first"
console.log(items[0]); // "first"
// Negative index (at() advantage!)
console.log(items.at(-1)); // "last"
console.log(items.at(-2)); // "fourth"
// With bracket notation you'd need:
console.log(items[items.length - 1]); // "last" - verbose!
Combining and Transforming
concat - Merge arrays (non-destructive):
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const arr3 = [7, 8, 9];
const merged = arr1.concat(arr2);
console.log(merged); // [1, 2, 3, 4, 5, 6]
console.log(arr1); // [1, 2, 3] - unchanged
// Concat multiple arrays
const all = arr1.concat(arr2, arr3);
console.log(all); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
// Modern alternative: spread operator
const spread = [...arr1, ...arr2, ...arr3];
console.log(spread); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
join - Convert to string:
const words = ["Hello", "World", "!"];
console.log(words.join(" ")); // "Hello World !"
console.log(words.join("-")); // "Hello-World-!"
console.log(words.join("")); // "HelloWorld!"
console.log(words.join()); // "Hello,World,!" (default: comma)
// Useful for paths and URLs
const pathParts = ["users", "123", "profile"];
console.log(pathParts.join("/")); // "users/123/profile"
reverse - Reverse order (destructive):
const numbers = [1, 2, 3, 4, 5];
numbers.reverse();
console.log(numbers); // [5, 4, 3, 2, 1] - mutated!
// To reverse without mutating:
const original = [1, 2, 3, 4, 5];
const reversed = [...original].reverse();
console.log(original); // [1, 2, 3, 4, 5]
console.log(reversed); // [5, 4, 3, 2, 1]
flat - Flatten nested arrays:
const nested = [1, [2, 3], [4, [5, 6]]];
console.log(nested.flat()); // [1, 2, 3, 4, [5, 6]] - one level
console.log(nested.flat(2)); // [1, 2, 3, 4, 5, 6] - two levels
console.log(nested.flat(Infinity)); // Flatten completely
// Practical use: remove empty slots
const sparse = [1, , 3, , 5];
console.log(sparse.flat()); // [1, 3, 5]
fill - Fill with values (destructive):
const arr = [1, 2, 3, 4, 5];
// fill(value, start, end)
arr.fill(0);
console.log(arr); // [0, 0, 0, 0, 0]
const arr2 = [1, 2, 3, 4, 5];
arr2.fill("x", 1, 4);
console.log(arr2); // [1, "x", "x", "x", 5]
// Create array of specific size with default value
const zeros = new Array(5).fill(0);
console.log(zeros); // [0, 0, 0, 0, 0]
Modern Immutable Methods (ES2023)
ES2023 introduced immutable versions of mutating methods. These return new arrays, leaving the original unchanged.
toSpliced - Immutable splice:
const original = ["a", "b", "c", "d", "e"];
// Remove and insert without mutating
const modified = original.toSpliced(1, 2, "x", "y");
console.log(modified); // ["a", "x", "y", "d", "e"]
console.log(original); // ["a", "b", "c", "d", "e"] - unchanged!
toReversed - Immutable reverse:
const numbers = [1, 2, 3, 4, 5];
const reversed = numbers.toReversed();
console.log(reversed); // [5, 4, 3, 2, 1]
console.log(numbers); // [1, 2, 3, 4, 5] - unchanged!
toSorted - Immutable sort:
const scores = [85, 92, 78, 95, 88];
const sorted = scores.toSorted((a, b) => b - a);
console.log(sorted); // [95, 92, 88, 85, 78]
console.log(scores); // [85, 92, 78, 95, 88] - unchanged!
with - Immutable index assignment:
const letters = ["a", "b", "c", "d"];
// Replace element at index (immutably)
const updated = letters.with(1, "X");
console.log(updated); // ["a", "X", "c", "d"]
console.log(letters); // ["a", "b", "c", "d"] - unchanged!
Quick Reference
| Method | Mutates? | Returns | Use Case |
|---|---|---|---|
| slice | No | New array | Extract portion |
| splice | Yes | Removed items | Add/remove/replace |
| push | Yes | New length | Add to end |
| pop | Yes | Removed item | Remove from end |
| unshift | Yes | New length | Add to start |
| shift | Yes | Removed item | Remove from start |
| concat | No | New array | Merge arrays |
| indexOf | No | Index or -1 | Find position |
| includes | No | Boolean | Check existence |
| reverse | Yes | Same array | Reverse order |
| toSpliced | No | New array | Immutable splice |
| toReversed | No | New array | Immutable reverse |
Takeaways
- slice copies, splice cuts - remember this and you’ll avoid 90% of array bugs
- Use push/pop for stack operations (end), shift/unshift for queues (beginning)
- includes is more readable than
indexOf !== -1for existence checks - at() with negative indices beats
arr[arr.length - 1]for accessing from the end - Prefer modern toSpliced, toReversed, toSorted when you need immutability
- When in doubt about mutation, check the return value - mutating methods often return the removed/modified data, not the array itself
Understanding which methods mutate and which create copies is fundamental to writing predictable JavaScript. When working with React or other frameworks that rely on immutability, prefer the non-mutating methods or create copies before modifying.
Frequently Asked Questions
What's the difference between splice and slice?
Slice creates a copy of a portion of an array without modifying the original. Splice modifies the original array by adding, removing, or replacing elements. Remember - slice is for copying (non-destructive), splice is for surgery (destructive).
Does splice return the removed elements or the modified array?
Splice returns an array of the removed elements, not the modified array. If no elements are removed, it returns an empty array. The original array is modified in place.
Can I use negative indices with slice?
Yes! Negative indices count from the end of the array. slice(-2) returns the last two elements, and slice(-3, -1) returns elements from third-to-last up to (but not including) the last element.
What are toSpliced, toSorted, and toReversed?
These are modern ES2023 methods that provide immutable alternatives to splice, sort, and reverse. They return new arrays instead of modifying the original, making them safer for functional programming patterns.
Should I use push/pop or unshift/shift?
Use push/pop when working with the end of an array (like a stack). Use unshift/shift for the beginning (like a queue). Push/pop are generally faster since arrays are optimized for operations at the end.
Stay in the loop
Get the latest updates on my blog, projects, and tech insights delivered straight to your inbox. No spam, unsubscribe anytime.
By subscribing, you agree to receive occasional updates. Unsubscribe anytime.