A guide to the 4 new Array.prototype methods in JavaScript
Tags: javascript, webdev
Published: 2023-08-10 18:01:42

**Written by [Ohans Emmanuel](https://blog.logrocket.com/author/ohansemmanuel/)✏️**

The latest version of the JavaScript language standard is ECMAScript 2023, which is the 14th edition. This update includes new methods on the `Array` prototype. 

I’ll guide you through the prominent four new methods in this article, including their behavior with sparse arrays and array-like objects. If you’re a fan of a declarative, functional style of writing JavaScript programs, you’re in for a treat. 

Dive in:

*   [Is it important to preserve the original array without any mutations?](#important-preserve-original-array-without-mutations)
*   [The `toReversed()` method](#toreversed-method)
*   [The `toSorted()` method](#tosorted-method)
*   [The `toSpliced(start, deleteCount, ...items)` method](#tospliced-start-deletecount-items-method)
*   [The `with(index, value)` method](#with-index-value-method)

## Is it important to preserve the original array without any mutations? <a name="important-preserve-original-array-without-mutations">

A common theme with the four new array methods is the focus on not mutating the original array, but returning a completely new array. You may wonder, why is this behavior significant? 

Generally speaking, there are numerous advantages to leaving data unmodified, as demonstrated by these four new array methods. These benefits are not limited to arrays, but rather extend to all JavaScript objects. 

Although there are many benefits, some of the most significant ones are outlined below:

*   **Pure functions**: In functional programming, pure functions are functions that always produce the same output when given the same input: they don’t have any side effects and their behavior is predictable. Working with this functional mental model is ideal when you're not modifying data, and these four new array methods are a great addition for this reason
*   **Predictable state management**: Creating new copies of our state object (or array) makes state management more predictable by eliminating unexpected changes and representing the data at a specific point in time with new copies. This simplifies managing the state at scale and improves reasoning about state management in general
*   **Change detection**: Frameworks like React use simplified change detection by comparing two copies of the state or props object to identify any alterations and render the user interface accordingly. Detecting changes becomes simpler with these methods, as we can compare the two objects at any given moment to identify any alterations

## The `toReversed()` method <a name="toreversed-method">

The `toReversed()` method is similar to the classic `reverse()` method, but with a significant distinction. `toReversed()` reverses the elements in an array, without mutating the original array. 

Consider the following array of fruits below:

```javascript
const fruits = ["🍎apple", "🍊orange", "🍌banana"]
```

Now, reverse `fruits` with `.reverse()`:

```javascript
// Reverse the array 
const result = fruits.reverse()
console.log(result) 

// ['🍌banana', '🍊orange', '🍎apple']

console.log(fruits)
// ['🍌banana', '🍊orange', '🍎apple']
// ↗️ original array is mutated
```

With `reverse()`, the original array is mutated. 

To reverse the array without mutating it, we can use the `toReversed()` method as demonstrated below:

```javascript
// Reverse the array 
const result = fruits.toReversed()
console.log(result) 

// ['🍌banana', '🍊orange', '🍎apple']

console.log(fruits)
// ["🍎apple", "🍊orange", "🍌banana"] 
// ↗️ original array is preserved
```

Voilà! 

If you’re using the latest version of a current browser like Chrome, you can access your browser console and test out the code examples provided in the article: ![Trying the toReversed method in the browser console](http://blog.logrocket.com/wp-content/uploads/2023/07/toreversed-method-browser-console.png) 

### Behavior with sparse arrays

For a quick refresher, sparse arrays are arrays without sequential elements. For example, consider the following:

```javascript
const numbers = [1,2,3]
// Assign an item to index 11
numbers[11] = 12

console.log(numbers)
// [1, 2, 3, empty × 8, 12]
```

In the example above, `numbers` has eight empty item slots. `numbers` is a sparse array. Now, back to `toReversed()`. How does this work with sparse arrays? 

`toReversed()` never returns a sparse array. If the original array had empty slots, they would be returned as `undefined`. 

Consider calling `toReversed()` on the `numbers` array below:

```javascript
const numbers = [1,2,3]
// Assign an item to index 11
numbers[11] = 12

numbers.toReversed()
// [12, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, 3, 2, 1]
```

As expected, all empty slots are returned as `undefined` array item values.

### Behavior with array-like objects

Even though `toReversed()` exists specifically on the `Array` prototype, it may also be invoked on array-like objects. 

An array-like object typically has a `length` property and, optionally, properties with integer index names. String objects are an example of array-like objects. 

The function `toReversed()` first reads the `length` property of the object it is called on and then iterates through the integer keys of the object from the end to the start, which means from `length - 1` to `0`. It adds the value of each property to the end of a new array, which is then returned. 

Let’s give this a try. Consider the wrong application of `toReversed()` on a string:

```javascript
const s = "Ohans Emmanuel"

// call `toReversed` directly on the string
s.toReversed()

//Uncaught TypeError: s.toReversed is not a function
```

Even though a string object is an array-like object, this program is wrong: we cannot invoke it in the manner `string.toReversed()` because `toReversed` doesn’t exist on the `string` prototype. 

However, we may use the `call()` method as shown below:

```javascript
const s = "Ohans Emmanuel"

// Array.prototype.toReversed.call(arrayLike)
Array.prototype.toReversed.call(s)

//['l', 'e', 'u', 'n', 'a', 'm', 'm', 'E', ' ', 's', 'n', 'a', 'h', 'O']
```

How about a self-constructed, array-like object? Consider the example below:

```javascript
// Has a length property and integer index property.
const arrayLike = {
 length: 5,
 2: "Item #2"
}
```

If this were a standard array, it would be a sparse array, i.e., of length five and a value in the second index. 

Consider the result of calling `toReversed` on this:

```javascript
console.log(Array.prototype.toReversed.call(arrayLike))

// [undefined, undefined, 'Item #2', undefined, undefined]
```

The `toReversed()` function produces a reversed array without creating a sparse array. As expected, the empty slots are returned as `undefined`.

## The `toSorted()` method <a name="tosorted-method">

`.toSorted()` is the counterpart to the classic `.sort()` method. 

As you may have guessed, unlike `.sort()`, `.toSorted()` will not mutate the original array. Consider the basic sort operation with `.sort()` below:

```javascript
const list = [1, 5, 6, 3, 7, 8, 3, 7]
//Sort in ascending order 
const result = list.sort()

console.log(result)
// [1, 3, 3, 5, 6, 7, 7, 8]
console.log(list)
// [1, 3, 3, 5, 6, 7, 7, 8]
```

As shown above, `sort()` sorts the array in place and consequently mutates the array. Now, consider the same with `toSorted()`:

```javascript
const list = [1, 5, 6, 3, 7, 8, 3, 7]
// Sort in ascending order 
const result = list.toSorted()

console.log(result)
// [1, 3, 3, 5, 6, 7, 7, 8]
console.log(list)
// [1, 5, 6, 3, 7, 8, 3, 7]
```

As seen above, `toSorted()` returns a new array with the elements sorted. 

Note that `toSorted()` retains the same syntax as `sort()`. For example, we may specify a function defining the sort order e.g., `list.toSorted(compareFn)`. 

Consider the example below:

```javascript
const list = [1, 5, 6, 3, 7, 8, 3, 7]
//Sort the array in descending order 
list.toSorted((a,b) => a < b ? 1 : -1)
// [8, 7, 7, 6, 5, 3, 3, 1]
```

### Behavior with sparse arrays

Empty slots will always be returned as `undefined`. In fact, they are treated as if they had a value of `undefined`. However, the `compareFn` will not be invoked for these slots and they’ll always come at the end of the returned array. 

Consider the following example with an array with an empty first slot:

```javascript
// Note the empty initial slot 
const fruits = [, "🍎apple", "🍊orange", "🍌banana"]

console.log(fruits.toSorted())

// ['🍊orange', '🍌banana', '🍎apple', undefined]
```

This behavior is identical to what would happen if the initial value were `undefined`. 

Consider the example below:

```javascript
const fruits = [undefined, "🍎apple", "🍊orange", "🍌banana"]

console.log(fruits.toSorted())

// ['🍊orange', '🍌banana', '🍎apple', undefined]
```

Also, note that the empty slots (or `undefined` slots) will always be moved to the end of the returned array, regardless of their position in the original array. 

Consider the following example:

```javascript
// empty slot is in index 2
const fruits = ["🍎apple", "🍊orange", , "🍌banana"]

console.log(fruits.toSorted())

// returned last 
// ['🍊orange', '🍌banana', '🍎apple', undefined]

// undefined value is in index 2

const otherFruits = ["🍎apple", "🍊orange", undefined , "🍌banana"]

console.log(otherFruits.toSorted())

// returned last 
// ['🍊orange', '🍌banana', '🍎apple', undefined]
```

### Behavior with array-like objects

When using the `toSorted()` function with objects, it will first read the `length` property of the `this` object. It will then collect the object's integer keys from the start to the end, which is from `0` to `length - 1`. After sorting them, it will return the corresponding values in a new array. 

Consider the following example with a string:

```javascript
const s = "Ohans Emmanuel"

// Array.prototype.toSorted.call(arrayLike)
Array.prototype.toSorted.call(s)
(14) [' ', 'E', 'O', 'a', 'a', 'e', 'h', 'l', 'm', 'm', 'n', 'n', 's', 'u']
```

Consider the following example with a constructed array-like object:

```javascript
// Has a length property and integer index property.
const arrayLike = {
 length: 5,
 2: "Item #2"
10: "Out of bound Item" // This will be ignored since the length is 5
}

console.log(Array.prototype.toSorted.call(arrayLike))
// ['Item #2', undefined, undefined, undefined, undefined]
```

## The `toSpliced(start, deleteCount, ...items)` method <a name="tospliced-start-deletecount-items-method">

`.toSpliced()` is the counterpart to the classic `.splice()` method. As with the other new methods we’ve covered, `toSpliced()` will not mutate the array it is invoked on, unlike `.splice()`. 

The syntax for `toSpliced` is identical to `.splice`, as shown below:

```javascript
toSpliced(start)
toSpliced(start, deleteCount)
toSpliced(start, deleteCount, item1)
toSpliced(start, deleteCount, item1, item2, itemN)
```

Add a new array item with the classic `.splice()`, as shown below:

```javascript
const months = ["Feb", "Mar", "Apr", "May"] 
// Insert item "Jan" at index 0 and delete 0 items
months.splice(0, 0, "Jan")

console.log(months) 
//  ['Jan', 'Feb', 'Mar', 'Apr', 'May']
```

`splice()` inserts the new array item and mutates the original array. To create a new array without mutating the original array, use `toSpliced()`. 

Consider the example above rewritten to use `toSpliced()`:

```javascript
const months = ["Feb", "Mar", "Apr", "May"] 
// Insert item "Jan" at index 0 and delete 0 items
const updatedMonths = months.toSpliced(0, 0, "Jan")

console.log(updatedMonths)
// ['Jan', 'Feb', 'Mar', 'Apr', 'May']
console.log(months)
// ['Feb', 'Mar', 'Apr', 'May']
```

`toSpliced()` returns a new array without mutating the original array. Note how the syntax for both `toSpliced()` and `splice()` are identical.

### Behavior with sparse arrays

`toSpliced()` never returns a sparse array. As such, empty slots will be returned as `undefined`. Consider the example below:

```javascript
const arr = ["Mon", , "Wed", "Thur", , "Sat"];
// Start at index 1, and delete 2 items
console.log(arr.toSpliced(1, 2)); 

// ['Mon', 'Thur', undefined, 'Sat']
```

### Behavior with array-like objects

With array-like objects, `toSpliced` gets the length of the `this` object, reads the integer key needed, and writes the result to a new array:

```javascript
const s = "Ohans Emmanuel"

// Start at index 0, delete 1 item, insert the other items
console.log(Array.prototype.toSpliced.call(s, 0, 1, 2, 3));

// [2, 3, 'h', 'a', 'n', 's', ' ', 'E', 'm', 'm', 'a', 'n', 'u', 'e', 'l']
```

## The `with(index, value)` method <a name="with-index-value-method">

The `.with()` array method is particularly interesting. First, consider the bracket notation for changing the value of a specific array index:

```javascript
const favorites = ["Dogs", "Cats"]
favorites[0] = "Lions"

console.log(favorites)
//(2) ['Lions', 'Cats']
```

With the bracket notation, the original array is always mutated. `.with()` achieves the same result of inserting an element in a specific index, but does not mutate the array. Instead, it returns a new array with the replaced index. 

Let’s rewrite the initial example to use `.with()`:

```javascript
const favorites = ["Dogs", "Cats"]
const result = favorites.with(0, "Lions")

console.log(result)
// ['Lions', 'Cats']
console.log(favorites)
// ["Dogs", "Cats"]
```

### Behavior with sparse arrays

`with()` never returns a sparse array. As such, empty slots will be returned as `undefined`:

```javascript
const arr = ["Mon", , "Wed", "Thur", , "Sat"];
arr.with(0, 2)
// [2, undefined, 'Wed', 'Thur', undefined, 'Sat']
```

### Behavior with array-like objects

Similar to other methods, `with()` reads the `length` property of the `this` object. It then reads every positive integer index (less than the `length`) of the object. As these are accessed, it saves their property values to the return array index. 

Finally, the `index` and `value` in the call signature `with(index, value)` are set on the returned array. Consider the example below:

```javascript
const s = "Ohans Emmanuel"

// Set the value of the first item
console.log(Array.prototype.with.call(s, 0, "F"));

// ['F', 'h', 'a', 'n', 's', ' ', 'E', 'm', 'm', 'a', 'n', 'u', 'e', 'l']
```

## Conclusion

The ECMAScript standard keeps improving, and taking advantage of its new features is a good idea. Go ahead and leverage `toReversed`, `toSorted`, `toSpliced`, and `with` to create more declarative JavaScript applications.

---
##[LogRocket](https://lp.logrocket.com/blg/javascript-signup): Debug JavaScript errors more easily by understanding the context

Debugging code is always a tedious task. But the more you understand your errors the easier it is to fix them.

[LogRocket](https://lp.logrocket.com/blg/javascript-signup) allows you to understand these errors in new and unique ways. Our frontend monitoring solution tracks user engagement with your JavaScript frontends to give you the ability to find out exactly what the user did that led to an error.

[![LogRocket Signup](https://blog.logrocket.com/wp-content/uploads/2020/06/reproduce-javascript-errors.gif)](https://lp.logrocket.com/blg/javascript-signup)

LogRocket records console logs, page load times, stacktraces, slow network requests/responses with headers + bodies, browser metadata, and custom logs. Understanding the impact of your JavaScript code will never be easier!

[Try it for free](https://lp.logrocket.com/blg/javascript-signup).