JavaScript arrays come with dozens of helpful methods. Chaining methods is also possible because some of the methods return an array instance, too. That means you can create your array pipelines and adjust the values to your needs by filtering, mapping, sorting, and so on.
But some of the array methods like sort
or reverse
mutate the array. Mutating the original array could cause side effects. For example, you may see correct results every second request because of a reverse operation.
This method shows you the newly introduced immutable JavaScript array methods.
JavaScript Series Overview
- Get the Current URL
- Get URL Query Parameters
- How to Smooth Scroll to an HTML Element
- Sort a Set
- New, Immutable Array Methods
- Detect Network Error
- Create Array from AsyncIterable
- Using the new Array Grouping Methods
- Test for LocalStorage Support
- Get Battery Status
- Detect If a Device Battery Is Charging (Coming soon)
The Problem: Mutating Array Methods
Some JavaScript array methods mutate the original array. For example, the sort
, reverse
, and pop
methods change the values within the original array:
const arr = [1, 4, 2, 3]
arr.sort()
console.log(arr)
// [1, 2, 3, 4]
arr.reverse()
console.log(arr)
// [4, 3, 2, 1]
arr.pop()
console.log(arr)
// [4, 3, 2]
Until now, you had to create an array copy with [...array]
or arr.slide()
first to not change the original array when using mutating methods. In the future, you don’t need to copy your array before working with it when using the new immutable array methods. Sweet!
Immutable Array Methods in JavaScript
JavaScript proposed new Array methods this year and runtimes including browsers already shipped them in new releases. We’re looking at the newly added immutable array methods.
Array#at(index)
The Array#at
method returns the item at a given index. You can also use a negative index. If the provided index is negative, JavaScript accesses index + array.length
. The at
method returns undefined
if the index is out of bounds or doesn’t match an item in the array:
const arr = [1, 4, 2, 3]
const first = arr.at(0) // 1
const last = arr.at(-1) // 3
const prelast = arr.at(-2) // 2
const unavailable = arr.at(10) // undefined
Array#with(index, value)
The Array#with
method replaces an array item at a given position with the provided value. You can’t add new items by using an index outside the bounds of the array. You can use negative values, JavaScript uses index + array.length
then:
const arr = [1, 4, 2, 3]
const newArr = arr.with(1, 10) // [1, 10, 2, 3]
const newArr = arr.with(-2, 10) // [1, 4, 10, 3]
💥 const newArr = arr.with(4, 10) // RangeError: Invalid index : 4 -> you’re out of bounds
Array#toSorted(compareFn)
The Array#toSorted
method is a copying, drop-in replacement for the sort
method. It returns a new array with the items sorted in ascending order. You may also provide a compareFn
defining the sort order:
const arr = [1, 4, 2, 3]
const newArr = arr.toSorted() // [1, 2, 3, 4]
const newArr = arr.toSorted((a, b) => b - a) // [4, 3, 2, 1]
Array#toReversed()
The Array#toReversed
method is a copying, drop-in replacement for the reverse
method. It returns a new array having its items in reversed order:
const arr = [1, 4, 2, 3]
const newArr = arr.toReversed() // [4, 3, 2, 1]
Array#toSpliced(start, deleteCount, ...items)
The Array#toSpliced
method is a copying, drop-in replacement for the splice
method. It returns a new array with items replaced and/or removed at a given index:
const arr = [1, 4, 2, 3]
const newArr = arr.toSpliced(2) // [1, 4]
const newArr = arr.toSpliced(2, 1) // [1, 4, 3]
const newArr = arr.toSpliced(2, 1, 10) // [1, 4, 10, 3]
const newArr = arr.toSpliced(2, 1, 10, 11, 12) // [1, 4, 10, 11, 12, 3]
Browser And Runtime Support
All modern browsers support the new methods. For example, you can check the caniuse.com for toSorted
to detect whether browsers support the method.
Node.js v20 and later support all the mentioned here.
Converting Mutating Methods to Non-Mutating Alternatives
We mentioned the mutating sort
, reverse
, and pop
methods in the beginning. JavaScript has more mutating methods. Here’s an overview of how you could replace the related methods in your code:
| Mutating Method | Non-Mutating Alternative |
|-------------------|----------------------------|
| pop()
| slice(0, -1)
or at(-1)
|
| push(v1, v2)
| concat(v1, v2)
|
| reverse()
| toReversed()
|
| shift()
| slice(1)
|
| sort()
| toSorted()
|
| splice()
| toSpliced()
|
| unshift(v1, v2)
| toSpliced(0, 0, v1, v2)
|
As you noticed, the non-mutating replacements are possibly harder to read. For example, replacing pop
with slice(0, -1)
makes me stop to think about the action that happens. Replacing pop
with at(-1)
seems easier to read and understand.
Also, there’s nothing wrong with mutating arrays when used correctly. Keep using the pop
method if you want to get and remove the last element from an array.
Use the @supercharge/arrays
Package
I’m the maintainer of the @supercharge/arrays package providing a fluent Array class. The exported Arr
class from @supercharge/arrays
works like the native JavaScript Array
. You’re wrapping an existing array, creating your call chain, and retrieving the final result as a native array using the toArray
method.
All methods in @supercharge/arrays
are immutable and don’t change the original array. Here’s an example using the sort
and reverse
methods to transform the wrapped values, but don’t mutate the source array:
import { Arr } from '@supercharge/arrays'
const arr = [1, 4, 2, 3]
const newArr = Arr.from(arr).sort().reverse().toArray()
// arr: [1, 4, 2, 3]
// newArr: [4, 3, 2, 1]
Enjoy new JavaScript array methods!
Mentioned Resources
- MDN docs für the
Array#at
method - MDN docs für the
Array#with
method - MDN docs für the
Array#toSorted
method - MDN docs für the
Array#toReversed
method - MDN docs für the
Array#toSpliced
method - caniuse.com for
Array#toSorted
- @supercharge/arrays repository on GitHub