JavaScript Slice Method in Depth
Tags: javascript, webdev, programming, tutorial
Published: 2022-10-17 12:50:54

**Author: <a  target="_blank" href="https://refine.dev/blog/author/abdullah_numan/">Abdullah Numan</a>**

## Introduction
This is a series on sparsely used iteration methods in JavaScript. In this series, we cover several methods that are used to iterate over a collection of data, act on them or produce side effects. These operations, especially with arrays and strings, are very common in front end development with libraries like React and Angular.



## What is Iteration?
Iteration is the process of looping through a collection of data and acting on each item in order to manipulate the item 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. `slice()`, `some()` and `every()` are such examples.

Some iteration methods, like `Array.prototype.forEach()` iterates over all items in a collection and some like `Array.prototype.slice()` iterates over the array partially.

We will cover several of these methods in this series, each in their own post.

In this article, we look into the details of using **JavaScript slice** which is available with identical implementations in `Array` and `String` prototypes. We'll get into their nuances with examples of both of these types.

Let's start with the array version of Javascript `slice()`.

## JS slice - `Array.prototype.slice()`
`Array.prototype.slice()` is an `Array` method that is used to extract a contiguous portion from an existing array. It can take two arguments: the `start` and the `end` indicator of the slice -- both of which are optional:

```ts
// Method signature

slice();
slice(start);
slice(start, end);
```

In this section, we set off with an example that expounds some general cases for selecting and storing a section from a source array. Then we'll see a couple of more interesting examples that help us generate arrays from arguments passed to a function using `Array.prototype.call()` and `Array.prototype.bind()`.

### Slicing an Array
A typical examples of slicing an array involves extracting a contiguous part from an existing array. Such as 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:

```ts
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 or negative offset values. The first one (`0` in the `firstThree` assignment above) represents the starting index or offset in the source array where slicing should begin and the second one (`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 from each slice:

```ts
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 part. And we should be careful that unlike in `Array.prototype.splice()`, whose second argument is the count of items to be deleted, the second argument of `Array.prototype.slice()` does not stand for the number of items to be extracted, rather the position before which slicing should stop.


### Other Nuances

**No Arguments**

 If we don't pass in any argument, we just get a **shallow copy** of the source array with all items:

```ts
const allCopied = elements.slice();

console.log(allCopied);
// (12) ["Please", "Send", "Cats", "Monkeys", "And", "Zebras", "In", "Large", "Cages", "Make", "Sure", "Padlocked"]
```

It's effectively `[...elements]`.


**No Second Argument**

If we don't pass in the second argument, the **JavaScript** slice extends to the last element:

```ts
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"]
```

Do notice that `lastThreeWithNoSecArg` evaluates to the same portion as `lastThree`, albeit missing the second argument.



<br/>
<div>
<a href="https://discord.gg/refine">
  <img src="https://refine.dev/img/discord_big_blue.png" alt="discord banner" />
</a>
</div>


**Negative Offsets**

As we've already seen above, we can pass in as arguments negative numbers that denote offset positions counted backwards from the last item. We can do this for both arguments:

```ts
const latestTwoBeforeLast = elements.slice(-3, -1);
console.log(latestTwoBeforeLast); // (2) ["Make", "Sure"]
```
Here, with `latestTwoBeforeLast`, we're getting the latest two items without the final one.


**Starting Position Greater Than Ending Position**

If we pass in a greater value for `start` than `end`, we get an empty array:

```ts
const somewhereWeDontKnow = elements.slice(5, 2);
console.log(somewhereWeDontKnow); // []
```

**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:

```ts
const somewhereInOuterSpace = elements.slice(15, 2);
console.log(somewhereInOuterSpace); // []
```

**Sparse Arrays**

If we our target portion has sparse items, they are also copied over:

```ts
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' ]
```

### Creating Arrays from a List of Arguments
Now we can go a bit crazy about slicing. Let's construct an array from a list of arguments passed to a function:

```ts
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()`.

That was easy.

The next level way of doing this is in the messiest possible way:

```ts
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 a overhead, but what were are doing is declaring two helper functions.

The first one, `boundSlice`, is derived from binding the `Function.prototype.call()` method with `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()`. If we then pass in an argument list as rest params to `boundSlice()`, `Array.prototype.slice()` is invoked on this array and a copy of the array is created from it.

In `createArray()`, we are accumulating the arguments and passing them to `boundSlice()` as `args`. So, whatever we pass to `createArray()` is returned as items inside an array.


## JS slice - `String.prototype.slice()`
Now, let's consider the  **JavaScript `slice()`** method for strings.

`String.prototype.slice()` does the exact same thing as `Array.prototype.slice()`, but with characters in a string.

Like `Array.prototype.slice()`, it takes two optional arguments, `start` and `end`:

```JavaScript
// Method signature

slice();
slice(start);
slice(end);
```

### Slicing a String

`String.prototype.slice()` is useful for directly working on strings. It removes the hassle of converting a string to an array with `Array.prototype.split()`:

```ts
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 negative offset values. Here, the first one, `0` in the `firstThree` assignment, stands for the starting index or offset of the portion and the second one (`3`) for the index or offset **before** which extraction should stop.


**With No Arguments**

If we don't pass in any arguments, we get a new copy of the whole string:

```ts
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"
```

**With No Second Argument**

If we don't pass the second argument, the `length` of the string becomes `end`:

```ts
const charsFromThird = mnemonic.slice(2);

console.log(charsFromThird);
// "ease Send Cats Monkeys And Zebras In Large Cages Make Sure Padlocked"
```

**Negative Offsets**

Similar to `Array.prototype.slice()`, negative values for `start` and `end` represent offset positions from the end of the array:

```ts
const lastThreeChars = mnemonic.slice(-3);
const latestTwoCharsBeforeLast = mnemonic.slice(-3, -1);

console.log(lastThreeChars); // "ked"
console.log(latestTwoCharsBeforeLast); // "ke"
```

**Starting Position Greater Than Ending Position or Length of Array**

Again, similar to `Array.prototype.slice()`, if `start` is greater than `end` or `length` of string, we get an empty string:

```ts
const inAnotherDimension = mnemonic.slice(5, 2);
const doingStringTheory = mnemonic.slice(15, 2);

console.log(inAnotherDimension); // ""
console.log(doingStringTheory); // ""
```

---

## Is your CRUD app overloaded with technical debt?

Meet the headless, React-based solution to build sleek **CRUD** applications. With refine, you can be confident that your codebase will always stay clean and boilerplate-free.

Try [refine](https://github.com/pankod/refine) to rapidly build your next **CRUD** project, whether it's an admin panel, dashboard, internal tool or storefront.


<div>
<a href="https://github.com/pankod/refine">
    <img  src="https://refine.dev/img/generic_banner.png" alt="refine blog logo" />
</a>
</div>

<br/>

---

## Conclusion
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 are used to extract contiguous portions from a source object representing these types.

We also saw that `String.prototype.slice()` is identical implementation of `Array.prototype.slice()` that removes the overhead of converting a string to an array of characters.

We also covered how function composition and context binding with `Function.prototype.call()` and `Function.prototype.bind()` allows us to define custom functions that help us generate arrays from a list of arguments.