Nerdz Logo

Bookmarks

JavaScript Splice, Slice, and Essential Array Methods

February 5, 2026   •   9 min read

JavaScript array methods illustration showing splice cutting into an array and slice creating a copy

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

MethodMutates?ReturnsUse Case
sliceNoNew arrayExtract portion
spliceYesRemoved itemsAdd/remove/replace
pushYesNew lengthAdd to end
popYesRemoved itemRemove from end
unshiftYesNew lengthAdd to start
shiftYesRemoved itemRemove from start
concatNoNew arrayMerge arrays
indexOfNoIndex or -1Find position
includesNoBooleanCheck existence
reverseYesSame arrayReverse order
toSplicedNoNew arrayImmutable splice
toReversedNoNew arrayImmutable 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 !== -1 for 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.

Newsletter

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.