This article was last updated on November 4, 2024 to include advanced use cases and improve the organization for a clearer understanding of the JavaScript slice() method.
Introduction
This post is about the JavaScript slice method, where we explore Array.prototype.slice
and String.prototype.slice
in depth.
The JavaScript Iteration Methods Series covers posts on iteration methods that are used to loop over a collection of data to act on the items or produce side effects from them. Iteration operations, especially with arrays and strings, are very common in front end development with libraries like React and Angular.js.
Steps we'll cover:
- Introduction
- What is Iteration ?
- Overview
- JS Slice -
Array.prototype.slice
- When to use JavaScript slice method?
- JS String Slice -
String.prototype.slice
- Advanced Use Cases for
slice()
- JavaScript - Best Practices Using JavaScript
slice()
- Summary
What is Iteration ?
Iteration is the process of looping through a collection of data and performing action on each item in order to manipulate it or create a side effect from it. Common examples of iteration methods in JavaScript are Array.prototype.forEach
, Array.prototype.map
and Array.prototype.reduce
.
There are other iteration methods which are not as often used, but when needed are always handy tools for iterating over arrays, strings and objects. JavaScript slice()
, some()
and every()
are such examples.
JavaScript Iteration Methods: Complete vs Partial Iteration
Some iteration methods, like Array.prototype.forEach
and Array.prototype.map
iterate over all items in a collection. Others like Array.prototype.slice
, Array.prototype.includes
or Array.prototype.some
iterate over the array partially, if not required completely.
Overview
In this post, we look into the details of using JavaScript slice which is an iteration method available with identical implementations in Array
and String
prototypes. We get into the nuances of the two versions with examples involving both of these object types.
We set off with an example that expounds some general cases for selecting and storing a section from a source array. Then we see a couple of more interesting examples that help us generate arrays from arguments passed to a function using Function.prototype.call
and Function.prototype.bind
.
Towards the end, we explore the usage and quirks of the String
version of JavaScript slice
with examples of extracting substrings from a source string.
Let's start with the array version of Javascript slice()
.
JS Slice - Array.prototype.slice
This section covers the usage of Array
version of JavaScript slice()
.
What is JavaScript Array slice
?
Array.prototype.slice
is a JS Array
method that is used to extract a contiguous subarray or "slice" from an existing array.
JavaScript slice
can take two arguments: the start
and the end
indicator of the slice -- both of which are optional. It can also be invoked without any argument. So, it has the following call signatures:
// Method signature
slice();
slice(start);
slice(start, end);
Slicing an Array: How to Use JavaScript slice
A typical example of slicing an array involves producing a contiguous section from a source array. For example, the first three items, last three items and some items spanning from a certain index up until another index.
As shown in the examples below:
const elements = [
"Please",
"Send",
"Cats",
"Monkeys",
"And",
"Zebras",
"In",
"Large",
"Cages",
"Make",
"Sure",
"Padlocked",
];
const firstThree = elements.slice(0, 3);
const lastThree = elements.slice(-3, elements.length);
const fromThirdToFifth = elements.slice(2, 5);
Both arguments of slice()
stand for zero-based index numbers. Negative values denote offset from the end of the array.
The first argument (0
in the firstThree
assignment above) represents the starting index or offset in the source array where slicing should begin. The second argument (3
) is the index or offset before which extraction should stop.
If we log the extracted values above, we can see the three elements we want for each slice:
console.log(firstThree); // ["Please", "Send", "Cats"]
console.log(lastThree); // ["Make", "Sure", "Padlocked"]
console.log(fromThirdToFifth); // ["Cats", "Monkeys", "And"]
It is important to notice that the item represented by the second argument is excluded from the extracted slice.
JavaScript Array slice
vs splice
We should also note that unlike in Array.prototype.splice
, the second argument of Array.prototype.slice
does not stand for the number of items to be extracted. In JavaScript splice
, the second argument is rather the count of items to be deleted, as opposed to being extracted.
Array.prototype.splice
is very dissimilar from Array.prototype.slice
because JavaScript splice()
mutates the original array, whereas slice()
creates a copy.
Nuances of JavaScript Array.prototype.slice
The sections below highlight with examples the nuances of how the JS Array slice
method behaves in scenarios with different argument values.
JavaScript Array slice
with No Arguments
If we don't pass any argument to JavaScript slice
, we get a shallow copy of the source array with all items:
const allCopied = elements.slice();
console.log(allCopied);
// (12) ["Please", "Send", "Cats", "Monkeys", "And", "Zebras", "In", "Large", "Cages", "Make", "Sure", "Padlocked"]
It's effectively [...elements]
.
JavaScript Array slice
Method with No Second Argument
If we don't pass the second argument, the extracted JavaScript Array slice extends to the last element:
const fromThird = elements.slice(2);
const lastThree = elements.slice(-3, elements.length);
const lastThreeWithNoSecArg = elements.slice(-3);
console.log(fromThird);
// (10) ["Cats", "Monkeys", "And", "Zebras", "In", "Large", "Cages", "Make", "Sure", "Padlocked"]
console.log(lastThree); // (3) ["Make", "Sure", "Padlocked"]
console.log(lastThreeWithNoSecArg); // (3) ["Make", "Sure", "Padlocked"]
Notice that lastThreeWithNoSecArg
evaluates to the same slice as lastThree
, albeit missing the second argument.
JavaScript Array.prototype.slice
with Negative Offsets
Notice also above that, we can pass in negative numbers as arguments. Negative values of the arguments denote offset positions counted backwards from the last item. We can do this for both arguments:
const latestTwoBeforeLast = elements.slice(-3, -1);
console.log(latestTwoBeforeLast); // (2) ["Make", "Sure"]
Here, with latestTwoBeforeLast
, we're getting the last two items prior to the final one.
JS Array.prototype.slice
: Starting Position Greater Than End Position
If we pass in a greater value for start
than end
, we get an empty array:
const somewhereWeDontKnow = elements.slice(5, 2);
console.log(somewhereWeDontKnow); // []
This indicates we have to always start slicing from a lesser positive index.
Array.prototype.slice
: Starting Position Greater Than Length of Array
Likewise, if we pass in a greater value for start
than array's length
, we get an empty array:
const somewhereInOuterSpace = elements.slice(15, 2);
console.log(somewhereInOuterSpace); // []
Using JS slice
with Sparse Arrays
If our target slice has sparse items, they are also copied over:
const elements = [
"Please",
"Send",
,
"Cats",
,
"Monkeys",
"And",
"Zebras",
"In",
"Large",
"Cages",
"Make",
"Sure",
"Padlocked",
];
const sparseItems = elements.slice(0, 6);
console.log(sparseItems);
// (6) [ 'Please', 'Send', <1 empty item>, 'Cats', <1 empty item>, 'Monkeys' ]
Array.prototype.slice
: Creating Arrays from a List of Arguments
In this section we go a bit crazy about slicing. We develop two interesting ways with Array.prototype.slice
to construct an array from a list of arguments passed to a function.
The first one:
const createArray = (...args) => Array.prototype.slice.call(args);
const array = createArray(1, 2, 3, 4);
console.log(array); // (4) [1, 2, 3, 4]
Here, we received args
as a list first, but converted it to an array with rest params ...args
. We then bound the array to Array.prototype.slice
with Function.prototype.call
and stored the function in createArray
.
Invoking createArray
with a list of arguments places them inside args
and a copy of it is created with Array.prototype.slice
. So, we get the new array as output.
That was easy.
The next level way of doing this is in the messiest possible way:
const boundSlice = Function.prototype.call.bind(Array.prototype.slice);
const createArray = (...args) => boundSlice(args);
const array = createArray(1, 2, 3, 4);
console.log(array); // (4) [1, 2, 3, 4]
It seems like an overhead, but what we are doing is declaring two helper functions.
The first one, boundSlice
, is derived from binding the Function.prototype.call
method with Function.prototype.bind
to Array.prototype.slice
which is an array function object. So, we are getting a copy of Function.prototype.call
bound to Array.prototype.slice
and storing it in boundSlice
.
This basically means, if we invoke boundSlice()
, we are in effect invoking Array.prototype.slice.call
. When we then pass in an array to boundSlice()
, Array.prototype.slice
is invoked and a copy is created from it.
In createArray()
, we are accumulating the items as rest arguments and passing them to boundSlice()
as args
array. So, whatever we pass to createArray()
is returned as items inside an array copied from args
.
When to use JavaScript slice method?
Creating a Subset of an Array: If you need a portion of an array without changing the original array,
slice
is the ideal choice.Copying an Array:
slice
can be used to create a shallow copy of an entire array. This is useful when you want to work with a copy of an array and leave the original array unchanged.String Manipulation: While often associated with arrays,
slice
is also applicable to strings. It's used to extract a substring from a string, based on the start and end indices provided.Creating Paginated Data: When implementing pagination,
slice
can be used to get the subset of data that should be displayed on the current page.Removing Elements Conditionally: While
slice
itself doesn't remove elements, it can be used in combination with other methods to conditionally remove elements from an array.Non-Destructive Array Manipulation: In scenarios where you need to ensure the original data structure remains unchanged,
slice
is preferable over methods likesplice
which modify the array in place.Working with the
arguments
Object: In functions, to convert thearguments
object into a real array,slice
can be used. This allows you to use Array methods onarguments
.
JS String Slice - String.prototype.slice
In this section, we explore the String version of JavaScript slice()
method with examples of creating substrins from a source string.
String.prototype.slice
does the exact same thing as Array.prototype.slice
, but with characters in a string as items.
Like Array.prototype.slice
, it takes two optional arguments, start
and end
. And it works without an argument as well:
// Method signature
slice();
slice(start);
slice(end);
Slicing a JavaScript String: How to Use String.prototype.slice
String.prototype.slice
is useful for directly working on strings. It removes the hassle of converting a string to an array with String.prototype.split
.
It can be used like below:
const mnemonic =
"Please Send Cats Monkeys And Zebras In Large Cages Make Sure Padlocked";
const firstThreeChars = mnemonic.slice(0, 3);
const lastThreeChars = mnemonic.slice(-3, mnemonic.length);
const fromThirdToFifthChars = mnemonic.slice(2, 5);
console.log(firstThreeChars); // "Ple"
console.log(lastThreeChars); // "ked"
console.log(fromThirdToFifthChars); // "eas"
Again, both arguments represent zero-based index numbers or offset values. Here too, the first argument -- 0
in the firstThree
assignment -- stands for the starting index or offset in the source array. And the second argument (3
) denotes the index or offset before which extraction should stop.
Advanced Use Cases for slice()
- JavaScript
I wanted to share a few interesting ways one might apply the slice()
method in JavaScript, other than how it's usually used. These examples might turn out handy, especially when working with more complex data.
Array Flattening
When one deals with a nested array and wants to extract a certain subarray, slice()
works without touching the original structure. Say you are working with just a few items at each nested level; slice()
can be used in a selectivity manner, to keep all things tidy and restrict the unwarranted changes to the main array.
const nestedArray = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
];
const slicedSubarray = nestedArray
.slice(0, 2)
.map((innerArr) => innerArr.slice(1));
console.log(slicedSubarray); // [[2, 3], [5, 6]]
Working with Array-like Objects
Array-like objects, such as the arguments in a function or NodeLists within a DOM, do not have all the various methods of arrays by default. Using the slice() method upon these can change them into real arrays, thereby opening many more functionalities. This is a neat way to deal with such structures without extra loops or conversions.
function getArgumentsArray() {
return Array.prototype.slice.call(arguments);
}
const argsArray = getArgumentsArray(1, 2, 3);
console.log(argsArray); // [1, 2, 3]
Using slice()
with Immutability
If you are working with libraries like Redux that rely on immutability, then it's a good use case for shallow copying arrays with slice()
, so you can update or do whatever you want with the data copy, leaving the original copy unchanged. This is the easy way out to keep your code predictable and functional programming principles conformant.
const originalArray = [1, 2, 3, 4, 5];
const modifiedArray = originalArray.slice(0, 3).concat([10, 11]);
console.log(modifiedArray); // [1, 2, 3, 10, 11]
console.log(originalArray); // [1, 2, 3, 4, 5]
Using slice()
in these methods has helped clean up code and make it more trustworthy, as well as offering far more control over the data.
JavaScript String Slice Nuances
Using JavaScript String slice
With No Arguments
Similar to Array slice
, if we don't pass any argument to String slice()
, the whole string is copied over:
const mnemonic =
"Please Send Cats Monkeys And Zebras In Large Cages Make Sure Padlocked";
const memorizedMnemonic = mnemonic.slice();
console.log(memorizedMnemonic);
// "Please Send Cats Monkeys And Zebras In Large Cages Make Sure Padlocked"
JS String slice
with No Second Argument
Here again, if we don't pass the second argument, the copied string extends to the end of the string:
const charsFromThird = mnemonic.slice(2);
console.log(charsFromThird);
// "ease Send Cats Monkeys And Zebras In Large Cages Make Sure Padlocked"
JavaScript String slice
with Negative Offsets
Similar to Array.prototype.slice
, negative values for start
and end
represent offset positions from the end of the array:
const mnemonic =
"Please Send Cats Monkeys And Zebras In Large Cages Make Sure Padlocked";
const lastThreeChars = mnemonic.slice(-3);
const latestTwoCharsBeforeLast = mnemonic.slice(-3, -1);
console.log(lastThreeChars); // "ked"
console.log(latestTwoCharsBeforeLast); // "ke"
JS String slice
with Higher Starting Positions
Again, if start
is greater than end
or the length
of string, we get an empty string:
const inAnotherDimension = mnemonic.slice(5, 2);
const doingStringTheory = mnemonic.slice(15, 2);
console.log(inAnotherDimension); // ""
console.log(doingStringTheory); // ""
Best Practices Using JavaScript slice()
Here's a quick look at how to avoid some common mistakes with JavaScript slice()
and get the most out of it. Best practices like these will help you keep your code clean and predictable, especially in projects where immutability and data integrity are valued.
Use slice()
for Immutability
slice()
is perfect when one needs to make a copy of an array without touching the original. If, for instance, you have state in some sort of Redux store or similar environment, slice()
will allow you to keep the original:
const originalArr = [1, 2, 3];
const copyArr = originalArr.slice();
copyArr.push(4);
console.log(originalArr); // [1, 2, 3]
console.log(copyArr); // [1, 2, 3, 4]
Combine slice()
with Other Array Methods
The slice()
method works well with methods like map()
or filter()
when you want to modify or extract part of an array without touching the whole thing. Here's an example:
const arr = [1, 2, 3, 4, 5];
const modifiedSubset = arr.slice(1, 4).map((num) => num * 2);
console.log(modifiedSubset); // [4, 6, 8]
Such an approach is especially convenient for creating views or paginated data sets from larger arrays.
Handle Sparse Arrays with Care
One thing to notice: slice()
keeps empty (sparse) array slots, and this can create some surprising behavior if you're dealing with incomplete data sets. If you want to lose those blank items, you can compose slice()
with filter(Boolean)
:
const sparseArr = [1, , 3, , 5];
const cleanArr = sparseArr.slice().filter(Boolean);
console.log(cleanArr); // [1, 3, 5]
This keeps the array clean without gaps, making it easier to process or display consistently.
Using these techniques with slice()
can make your code more readable and help avoid some of the widely occurring array problems.
Summary
In this post, we expounded the slice()
method in JavaScript. We saw that JavaScript implements slice()
in two flavors: one for Array
s with Array.prototype.slice
and one for String
s with String.prototype.slice
. We found out through examples that both methods produce a copy of the source object and they are used to extract a target contiguous slice from it.
We covered a couple of examples of how function composition and context binding with Function.prototype.call
and Function.prototype.bind
allows us to define custom functions using Array.prototype.slice
to help us generate arrays from a list of arguments.
We also saw that String.prototype.slice
is an identical implementation of Array.prototype.slice
that removes the overhead of converting a string to an array of characters.