JavaScript Array Slice Method: A Complete Guide

Master the JavaScript slice() method for extracting portions of arrays without mutation. Covers syntax, negative indices, shallow copying, real-world extraction patterns, and how slice differs from splice.

JavaScriptbeginner
11 min read

The slice() method is one of the most important non-mutating array methods in JavaScript. It extracts a section of an array and returns it as a new array, leaving the original untouched. This makes slice() essential for immutable data patterns in React, Redux, and any functional programming approach. If you have been using push(), pop(), shift(), and unshift() to manipulate arrays, slice() is the tool you reach for when you need extraction without side effects.

Syntax

javascriptjavascript
array.slice()
array.slice(start)
array.slice(start, end)
ParameterTypeDescription
startNumber (optional)Index where extraction begins (inclusive). Defaults to 0
endNumber (optional)Index where extraction stops (exclusive). Defaults to array.length

Key rule: start is inclusive, end is exclusive. The element at the end index is not included in the result.

Basic Usage

javascriptjavascript
const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun"];
 
// Extract from index 1 to 4 (not including 4)
const spring = months.slice(1, 4);
console.log(spring); // ["Feb", "Mar", "Apr"]
 
// Original is unchanged
console.log(months); // ["Jan", "Feb", "Mar", "Apr", "May", "Jun"]

One Argument: Slice From Index to End

javascriptjavascript
const numbers = [10, 20, 30, 40, 50];
 
const fromTwo = numbers.slice(2);
console.log(fromTwo); // [30, 40, 50]
 
const fromFour = numbers.slice(4);
console.log(fromFour); // [50]

No Arguments: Shallow Clone

Calling slice() with no arguments returns a shallow copy of the entire array:

javascriptjavascript
const original = [1, 2, 3, 4, 5];
const clone = original.slice();
 
clone.push(6);
console.log(original); // [1, 2, 3, 4, 5] — unchanged
console.log(clone);    // [1, 2, 3, 4, 5, 6]

This is equivalent to [...original] or Array.from(original), but slice() was available long before ES6.

Negative Indices

Negative values count backward from the end of the array. -1 refers to the last element, -2 to the second-to-last, and so on:

javascriptjavascript
const letters = ["a", "b", "c", "d", "e"];
 
// Last 2 elements
console.log(letters.slice(-2));    // ["d", "e"]
 
// From third-to-last to second-to-last
console.log(letters.slice(-3, -1)); // ["c", "d"]
 
// Everything except the last element
console.log(letters.slice(0, -1)); // ["a", "b", "c", "d"]
ExpressionStart (resolved)End (resolved)Result
slice(-2)35["d", "e"]
slice(-3, -1)24["c", "d"]
slice(0, -1)04["a", "b", "c", "d"]
slice(1, -2)13["b", "c"]

Edge Cases and Boundary Behavior

Understanding how slice() handles unusual inputs prevents surprises:

javascriptjavascript
const arr = [10, 20, 30, 40, 50];
 
// Start beyond length: empty array
console.log(arr.slice(10));     // []
 
// End beyond length: same as slicing to the end
console.log(arr.slice(2, 100)); // [30, 40, 50]
 
// Start >= end: empty array
console.log(arr.slice(3, 1));   // []
 
// Both negative, start < end
console.log(arr.slice(-4, -2)); // [20, 30]
 
// Undefined parameters: treated as defaults
console.log(arr.slice(undefined, 3)); // [10, 20, 30]
ScenarioInputResultWhy
Start past endslice(10)[]No elements from index 10 onward
End past lengthslice(2, 100)[30, 40, 50]Clamped to array length
Start >= endslice(3, 1)[]Zero-width range
Both undefinedslice()Full cloneDefaults to (0, length)

Shallow Copy Behavior

slice() creates a shallow copy, meaning it copies element references, not the elements themselves. This matters when your array contains objects:

javascriptjavascript
const users = [
  { name: "Alice", score: 95 },
  { name: "Bob", score: 82 },
  { name: "Carol", score: 91 },
];
 
const topTwo = users.slice(0, 2);
 
// Modifying the object inside the copy affects the original
topTwo[0].score = 100;
console.log(users[0].score); // 100 — original object was mutated!
 
// But adding to the copy does not affect the original array
topTwo.push({ name: "Dave", score: 77 });
console.log(users.length);  // 3 — unchanged
console.log(topTwo.length); // 3
Shallow Copy Trap

Slicing an array of objects gives you a new array, but the objects inside are still shared references. If you need truly independent copies, use structuredClone() or map with object spread: arr.map(obj => ({ ...obj })).

Real-World Patterns

Pagination

Extracting a page of results from a dataset:

javascriptjavascript
function paginate(items, page, pageSize) {
  const start = (page - 1) * pageSize;
  const end = start + pageSize;
  return {
    data: items.slice(start, end),
    page,
    totalPages: Math.ceil(items.length / pageSize),
    totalItems: items.length,
  };
}
 
const products = Array.from({ length: 47 }, (_, i) => ({
  id: i + 1,
  name: `Product ${i + 1}`,
}));
 
const page2 = paginate(products, 2, 10);
console.log(page2.data.length); // 10
console.log(page2.data[0].name); // "Product 11"
console.log(page2.totalPages);   // 5

Removing the First or Last N Elements (Immutably)

javascriptjavascript
const items = ["a", "b", "c", "d", "e"];
 
// Remove first element (immutable shift)
const withoutFirst = items.slice(1);
console.log(withoutFirst); // ["b", "c", "d", "e"]
 
// Remove last element (immutable pop)
const withoutLast = items.slice(0, -1);
console.log(withoutLast); // ["a", "b", "c", "d"]
 
// Remove first 2 and last 2
const middle = items.slice(2, -2);
console.log(middle); // ["c"]
 
// Original unchanged in all cases
console.log(items); // ["a", "b", "c", "d", "e"]

Capping Array Size

Keeping only the most recent N items without mutation:

javascriptjavascript
function keepRecent(items, maxCount) {
  if (items.length <= maxCount) return items;
  return items.slice(-maxCount);
}
 
const logs = ["event1", "event2", "event3", "event4", "event5", "event6"];
const recent = keepRecent(logs, 3);
console.log(recent); // ["event4", "event5", "event6"]

Converting Arguments to a Real Array

Before rest parameters existed, slice was the standard way to convert the arguments object into a real array:

javascriptjavascript
// Legacy pattern (pre-ES6)
function legacy() {
  const args = Array.prototype.slice.call(arguments);
  return args.map(a => a.toUpperCase());
}
 
// Modern equivalent
function modern(...args) {
  return args.map(a => a.toUpperCase());
}
 
console.log(legacy("hello", "world"));  // ["HELLO", "WORLD"]
console.log(modern("hello", "world"));  // ["HELLO", "WORLD"]

slice() vs splice(): The Critical Difference

These two methods have similar names but completely different behavior. This confusion is one of the most common sources of bugs for JavaScript developers. A dedicated comparison is covered in the slice vs splice article, but here is a quick reference:

Featureslice()splice()
Mutates originalNoYes
PurposeExtract a portionAdd, remove, or replace elements
ReturnsNew array (extracted portion)Array of removed elements
Parameters(start, end)(start, deleteCount, ...items)
End parameterExclusive (not included)N/A (uses count, not end index)
javascriptjavascript
const arr = [1, 2, 3, 4, 5];
 
// slice: non-destructive extraction
const sliced = arr.slice(1, 3);
console.log(sliced); // [2, 3]
console.log(arr);    // [1, 2, 3, 4, 5] — unchanged
 
// splice: destructive modification
const spliced = arr.splice(1, 2);
console.log(spliced); // [2, 3]
console.log(arr);     // [1, 4, 5] — modified!

slice() with Strings

JavaScript strings also have a slice() method with identical parameter behavior:

javascriptjavascript
const text = "JavaScript";
 
console.log(text.slice(0, 4));  // "Java"
console.log(text.slice(4));     // "Script"
console.log(text.slice(-6));    // "Script"

The consistency between string and array slice() makes it easy to remember: start inclusive, end exclusive, negatives count from the end.

Performance

slice() runs in O(k) time where k is the number of elements being copied. For a full clone, that is O(n). The operation is a single memory allocation plus a shallow copy, making it fast for typical array sizes:

javascriptjavascript
// Slicing 10,000 elements from a 1M array: ~0.1ms
const huge = Array.from({ length: 1_000_000 }, (_, i) => i);
console.time("slice-10k");
const portion = huge.slice(0, 10_000);
console.timeEnd("slice-10k"); // ~0.05-0.15ms

Common Mistakes

Forgetting that end is exclusive:

javascriptjavascript
const arr = [10, 20, 30, 40, 50];
 
// Bug: developer wants elements at indices 1, 2, 3
const wrong = arr.slice(1, 3);  // Only gets indices 1, 2
console.log(wrong); // [20, 30] — missing 40!
 
// Fix: add 1 to end parameter
const right = arr.slice(1, 4);
console.log(right); // [20, 30, 40]

Assuming slice creates a deep copy:

javascriptjavascript
const items = [{ count: 1 }, { count: 2 }];
const copy = items.slice();
copy[0].count = 99;
console.log(items[0].count); // 99 — both reference the same object
Rune AI

Rune AI

Key Insights

  • Non-mutating: slice() never changes the original array; it always returns a new one
  • Inclusive start, exclusive end: slice(1, 4) includes indices 1, 2, and 3, not 4
  • Negative indices: Count from the end; slice(-3) returns the last three elements
  • Shallow copy: Object references are shared between the original and sliced arrays
  • Versatile patterns: Use slice() for pagination, immutable removal, cloning, and capping array size
RunePowered by Rune AI

Frequently Asked Questions

Does slice() modify the original array?

No. `slice()` always returns a new array and never modifies the original. This is its most important characteristic and the main reason to use it over `splice()` when you only need to extract data.

What is the difference between slice() and splice()?

`slice()` extracts elements without changing the array (non-mutating, takes start and end index). `splice()` adds, removes, or replaces elements in place (mutating, takes start index and delete count). They are fundamentally different operations despite the similar names.

How do I copy an array with slice()?

Call `slice()` with no arguments: `const copy = original.slice()`. This creates a shallow copy where primitive values are independent but object references are shared. For a deep copy, use `structuredClone(original)` instead.

Can I use slice() with negative numbers?

Yes. Negative numbers count from the end of the array. `slice(-3)` returns the last 3 elements, and `slice(-3, -1)` returns elements from the third-to-last up to (but not including) the last element.

Is slice() or spread faster for copying arrays?

Performance is nearly identical in modern engines. Both create a shallow copy in O(n) time. Use spread (`[...arr]`) for readability in modern code, and `slice()` when you also need to extract a subset with start/end parameters.

Conclusion

The slice() method is the backbone of non-mutating array operations in JavaScript. It extracts any contiguous portion of an array by start and end index, supports negative indices for counting from the end, and always returns a new array. Its non-destructive nature makes it essential for React state updates, functional programming patterns, pagination, and any scenario where the original data must remain intact. The most critical detail to internalize is that end is exclusive, and that the copy is shallow when the array contains objects.