DEV Community

Cover image for Why You Should Understand Closures To Master .reduce()
Vasil Vasilev
Vasil Vasilev

Posted on • Edited on

Why You Should Understand Closures To Master .reduce()

Although closures are one of the most potent features of Javascript, in my experience, they seem to be neglected in many explanations as if the reader is to infer what is under the hood on their own. If you look at a few articles about .reduce(), they may explain how we define it, the mechanism behind it, but not how it is possible for .reduce() to happen in the first place. This has proved especially irritating for me, in the beginning, since it left a feeling that I have to learn the concept by heart and I had to revisit the basic explanation after some time has passed.

So what is the basic definition?

The reduce() method executes a user-supplied "reducer" callback function on each element of the array, in order, passing in the return value from the calculation on the preceding element. The final result of running the reducer across all elements of the array is a single value.

What would a basic code example look like?

let arr = [1, 2, 3];

const arrSum = arr.reduce(
    (totalOrAccumulator, currentValueToAddToTotalOrAccumulator) => {
        totalOrAccumulator + currentValueToAddToTotalOrAccumulator
    }
)

console.log(arrSum)

Enter fullscreen mode Exit fullscreen mode

The fundamental question is - how does .reduce() keep record of totalOrAccumulator's value during execution?

The answer - closures.

But before that we must take into account another "oddity" with the code above.
.reduce() is a higher-order function since it returns another function (callback fn), that being the one that actually has the parameters and the logic of accumulating the total value.

This is a crucial point because closures provide a way for the callback function to maintain and update the state of the accumulator throughout the reduction process, resulting in the desired outcome.

It is also a process that is hidden when implementing .reduce() and although that is a out of necessity for clean and succinct code, beginners may find it hard to wrap their head around what actually enables .reduce() to keep track of the total value (accumulator).

It is hidden because the initial value is optional but it is the definite mark of closures in .reduce().

initialValue - mark of closures

The following code is an example of reduce used with initialValue and it is a mark of closures, too, for the keen eye.

const array1 = [1, 2, 3, 4];

// 0 + 1 + 2 + 3 + 4
const initialValue = 0;
const sumWithInitial = array1.reduce(
  (accumulator, currentValue) => accumulator + currentValue,
  initialValue
);

console.log(sumWithInitial);
Enter fullscreen mode Exit fullscreen mode

Basic closure example:

function closureFunction () {
    const number = 0;
    function addToNumber(addedNumber) {
        return number + addedNumber;
    }

    return addToNumber
}

const addFive = closureFunction();

console.log(addFive(5));

Enter fullscreen mode Exit fullscreen mode

The code above reveals the fundamental parts of a closure:

  1. a higher-order function that returns another function
  2. a variable that is accessed by the returned function via lexical scoping
  3. explicit return of the returned function
  4. storing of the closure function in a variable

The effect of the four steps above results in the greatest power of closures - we can access the variable stored in the closure function even after its execution. The reason for that is for a whole new article, but simply said, that variable (number in our case) is stored on the heap memory, not stack memory.

Comparing the closure example and the .reduce() example with intialValue, we can clearly see how a variable maintains and updates the state of the accumulator throughout the reduction process.

Top comments (0)