JavaScript Closures and Custom Iterators: A Comprehensive Guide.
Tags: javascript, programming, webdev, beginners
Published: 2023-02-17 16:20:51

## Demystifying Closures in JavaScript

Closures are one of the most elegant and powerful features of JavaScript, allowing developers to write expressive and concise code. However, despite their potential, closures are often misunderstood and widely misinterpreted. In this blog, we will demystify closures, taking a deep dive into their inner workings and exploring how to create iterators using closures. By the end, you'll be equipped to harness the full power of closures in your own projects.

Let's start with the basics:

## JavaScript and Lexical Scoping
**JavaScript is a lexically scoped language**, which fundamentally means that the data available when calling a function is determined by where it was originally defined. In other words, **where the function was defined is what determines the data it has access to**. If the available information varies depending on where it is called, we would be talking about a dynamic scope... but this is not the case.

Once this concept is understood, we can proceed with closures.

## How closures work

**Closures are formed from a function that returns a function**, which is where functional programmers would be delighted with me. The inner function in this case is able to **access variables and parameters from the outer function, even after the outer function has returned**. This is possible because the inner function has formed a bond with its surrounding data, called **closure or lexical closure**.

In other words, **the inner function has its own backpack**, in which it carries around the data from its surrounding scope. When the inner function is returned, it retains access to that backpack, allowing it to access the same variables and parameters from the outer function whenever it is called.

To illustrate this concept, let's take a look at an example:

![Two functions, one inside the other, in the inside function declaration the bond called closure gets solidified, and it's shown with something like a marker lines. ](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1t6l3u0q8fyta5sr9awh.png)

In this example, **outerFunc** defines a variable **outerVar**, and then returns the **innerFunc**. When **innerFun** is called, it logs the value of **outerVar**, even though it is no longer in the scope of **outerFunc**.

With closures, we can create powerful and flexible code. One example of this is the ability to create **custom iterators**, which allow us to iterate over data in a flexible and efficient way. With custom iterators, we can create complex operations on data with ease, giving JavaScript the full power of classic iteration libraries like Python's itertools.

But that's just the tip of the iceberg. **With closures, you have infinite possibilities**. You can create reusable functions that are tailored to your specific needs, or you can create encapsulated modules that protect your code from global namespace pollution. The only limit is your imagination.

In the next section, we'll explore how to create custom iterators using closures. Get ready to harness the full power of closures!

Let's take a look at a code example:

```

function createFlow(array) {
    let i = 0;
    function inner(){
        const element = array[i];
        i++;
        return element;
    }
    return inner;    
};

const returnNextElement = createFlow([4,5,6]);

const element1 = returnNextElement()
const element2 = returnNextElement()

```
Now things get interesting because **element1** is calling **returnNextElement()**, which has the following functionality within it:

```

function inner() {
    const element = array[i];
    i++;
    return element;
}

```
But where is the array? We haven't defined it anywhere in the current execution context, so it should be **undefined**, right?

Here's where closures come in. As soon as we defined **inner()** inside **createFlow()**, it formed a bond with all the surrounding live memory, in other words, the surrounding data. When we returned that function, we returned in its backpack all of its surrounding data, which in this case happens to be the array **[4,5,6]**, giving us access to its indexes.

So instead of looking for the array in the global memory, we look in the backpack of our returned function.

By the way, the posh name for **returnNextElement** is **iterator**. Iterators turn our collection data into streams, i.e., flows of actual values that we can access one after another.

With closures, we have the ability to create custom iterators, giving JavaScript the full power of classic iteration libraries like Python's itertools.

## Creating Custom Iterators with Closures
In JavaScript, iterators are objects that provide a sequence of values from a collection. An iterator has a method called **next** which returns an object with two properties: **value** and **done**. The **value** property contains the next value in the sequence, and the **done** property is a boolean value that indicates whether the iterator has reached the end of the sequence.

With closures, we can easily create custom iterators that can iterate over any data type. Here's an example:
```
function returnIterator(arr) {
  let i = 0;
  function indIterator(){
    const element = arr[i];
    i ++;
    return element;
  }
  return indIterator;
}

const array = ['a', 'b', 'c', 'd'];
const myIterator = returnIterator(array);
console.log(myIterator()); // -> should log 'a'
console.log(myIterator()); // -> should log 'b'
console.log(myIterator()); // -> should log 'c'
console.log(myIterator()); // -> should log 'd'
```
In this example, **returnIterator** is a function that takes an array and returns an iterator function. The iterator function **indIterator** has access to the **arr** variable in its closure and returns the next element of the array each time it is called.

We can also create an object with a **next** method to make our custom iterator more precise. Here's an example:
```
function nextIterator(arr) {
  let i = 0;
  const iterator = {
    next: function () {
      const value = arr[i];
      i++;
      return {
        value: value,
        done: i > arr.length
      };
    }
  };
  return iterator;
}

const array = ['a', 'b', 'c', 'd'];
const myIterator = nextIterator(array);
console.log(myIterator.next()); // -> should log { value: 'a', done: false }
console.log(myIterator.next()); // -> should log { value: 'b', done: false }
console.log(myIterator.next()); // -> should log { value: 'c', done: false }
console.log(myIterator.next()); // -> should log { value: 'd', done: false }
console.log(myIterator.next()); // -> should log { value: undefined, done: true }
```
In this example, **nextIterator** returns an object with a **next** method. Each time the next method is called, it returns an object with a **value** property and a **done** property. The **value** property is the next element in the array, and the **done** property is a boolean value that indicates whether the iterator has reached the end of the array.

## Conclusion
Closures are a powerful feature in JavaScript that allow us to write more expressive and concise code. They enable us to create custom iterators, which are essential for many programming tasks. With closures, we can create reusable functions that are tailored to our specific needs, or we can create encapsulated modules that protect our code from global namespace pollution. The only limit is our imagination.