Reverse an Array in JavaScript

Hero image for Reverse an Array in JavaScript. Image by cdd20.
Hero image for 'Reverse an Array in JavaScript.' Image by cdd20.

It's really common to be tasked with reversing an array in JavaScript, whether you're displaying items in reverse order or processing data from end to start. As you will probably know, JavaScript offers the inbuilt .reverse() method, which makes this task simple. However, there is more to reversing arrays than meets the eye, especially when it comes to immutability.

Today, I intend to explore how to reverse arrays in JavaScript, the caveats of using .reverse(), and touch upon some of the older methods that you might encounter for reversing arrays. By the end, you should hopefully understand the best approaches for reversing arrays without unintended side effects.


The .reverse() Method

Any relatively seasoned frontend developer will be familiar with the .reverse() method. This is the most straightforward way to reverse an array in JavaScript, and looks something like this:

const originalArray = [1, 2, 3, 4, 5];const reversedArray = originalArray.reverse();console.log(reversedArray);  // Output: [5, 4, 3, 2, 1]

At first glance, this all looks very straightforward and perhaps unusually for JavaScript development very logical. You might appreciate, though; this wouldn't exactly be a very long or interesting article if there wasn't at least one catch. In this case, there's a critical detail to be aware of: .reverse() mutates the original array.

The Mutation Problem

In layman's terms, a mutating method changes the original source data as well (our array). This means that your original (named) variable is now backwards, which can very quickly lead to unexpected behaviour, especially when the same array is referenced elsewhere in your code:

const numbers = [1, 2, 3, 4, 5];const reversed = numbers.reverse();console.log(numbers);  // Output: [5, 4, 3, 2, 1] (original array is modified)

As you can see in the example above, even though we've defined a new const (reversed) for the backwards version of the array, the original const (numbers) has also now been reversed (mutated).

This is a side effect that can cause bugs, especially in larger applications where data immutability is crucial.

Reversing Without Mutating

So, to reverse an array without mutating the original, we need to create a shallow copy first. The most common approach for this is to use the spread operator, like this:

const originalArray = [1, 2, 3, 4, 5];const reversedArray = [...originalArray].reverse();console.log(originalArray);  // Output: [1, 2, 3, 4, 5] (unchanged)console.log(reversedArray);  // Output: [5, 4, 3, 2, 1]

With this approach, we can maintain immutability, and align our practices with those used in methods like .map() and .filter(), which return new arrays rather than altering the original.

Why Immutability Matters

It may well be that you never intend to use the original const again, so it doesn't matter for your specific usecase. However, immutability helps prevent unintended side effects and makes our code easier to:

  • Debug:

    Since data isn't unexpectedly changing, it's easier to track down issues.
  • Maintain:

    Immutability leads to more predictable code, reducing hidden dependencies.
  • Optimise:

    Many modern frameworks (like React) rely on immutability for performance optimisations.

Alternative Methods of Reversing an Array

The .reverse() method has been available since ECMAScript 1 (1997), so it is safe to say that you are going to struggle to find any JavaScript environment in use today that doesn't support it it quite literally predates even IE6! Nevertheless, it would be amiss of for me to not at least explore legacy solutions with JavaScript fundamentals.

Using a for Loop

The most straightforward legacy alternative to .reverse() would be to use a for loop, like this:

const reverseArray = (arr: number[]): number[] => {  const result: number[] = [];  for (let i = arr.length - 1; i >= 0; i--) {    result.push(arr[i]);  }  return result;};const original = [1, 2, 3, 4, 5];console.log(reverseArray(original));   // Output: [5, 4, 3, 2, 1]

Here we simply loop over the original array backwards, pushing our new order into a new variable. This approach doesn't mutate the original array and is still efficient, although perhaps less concise than using the spread operator and .reverse().

Using reduce()

Using reduce() which wasn't introduced until December 2009 as part of ECMAScript 5 certainly isn't going to win us any 'legacy alternative to .reverse()' rewards. Even so, it does offer an alternative that removes mutation, and is worth exploring simply as a way to discuss the way JavaScript works and the many ways it can be used to achieve the same thing. I wouldn't even suggest this is a viable production alternative, just an experiment and discussion point.

So, using reduce() to reverse an array looks like this:

const originalArray = [1, 2, 3, 4, 5];const reversedArray = originalArray.reduce((acc, curr) => [curr, ...acc], []);console.log(reversedArray);  // Output: [5, 4, 3, 2, 1]

This method avoids mutation, but it is less performant for large arrays due to the repeated use of the spread operator inside of the accumulator. As I say, it's not worth attempting in a production environment!


Wrapping up

Reversing an array in JavaScript is straightforward with .reverse(), but being aware of its mutating behaviour is crucial and something I've often seen developers tripped up by. By using techniques like spreading into a new array, you can maintain immutability, making your code more predictable and maintainable.

Key Takeaways

  • The .reverse() method mutates the original array.
  • Use [...array].reverse() to reverse without mutation, promoting immutability.
  • Older methods like for loops, and newer methods like reduce() offer alternative, nonmutating solutions but probably wouldn't be suitable in production.
  • .reverse() has been available since ECMAScript 1 (1997), so it's universally supported.

Understanding both modern and traditional approaches gives you flexibility and deeper insight into JavaScript's capabilities.


Categories:

  1. Development
  2. Front‑End Development
  3. Guides
  4. JavaScript