Learn JavaScript Closures in 7 mins
Tags: javascript, webdev, beginners, tutorial
Published: 2021-11-16 07:04:03

To learn closures, you need to understand how scope works.

In JavaScript, we have global and local scope.

Variables declared in the main body are referred to as global scopes. They belong to the global object and can be accessed from anywhere in the code.

Variables declared in a function are referred to as local scopes. They belong to the function body including its nested functions, and they can access any variable in the global scope.

If a function is defined inside a function, the parent function will not have access to the variables declared in the child function. But the child function will have access to the variables in the parent function.

So basically any block of code has access to the variables of its outer scope.

Here is an example

```js
const x = 'someone';
function incrementFrom(count) {
  // has access to x
  return count++;
}

const firstCall = incrementFrom(0);
const secondCall = incrementFrom(5);
console.log(firstCall);
console.log(secondCall);

// does not have access to count i.e console.log(count) will throw an error
```

Have in mind, that the parameter is a local variable because it's in a local scope. It will get created anew for every call to the `incrementFrom` function.
It is basically the same thing as

```js
function incrementNum() {
  let count = 5;
  return count++;
}
// all the calls
```

So the good news is that local variables will not trample each other when the function is called.

But the bad news is, with such standard calling `incrementFrom(5)` a couple of times will not increment it. It's just going to keep logging 5, because "every local variable is created anew for every call".

So what if we want to keep making use of the value passed into (or created in) a function for each call to this function. Like in the case of `incrementFrom()`, we simply want to get an initial value and increment on it for every call.

So when I call `incrementFrom(3)` 3 times, it will magically increment from 3 to 4, then to 5, and to 6. This is possible with closures.

Another example is probably to pass in a `firstName` of a user to a function, and then later add the `lastName` to it. Something like

```js
function printName(firstName, lastName) {
  return `${firstName} ${lastName}`;
}
```

For some reasons, `lastName` has not been provided yet, so you make the first call with what you have now

```js
console.log(printName('John', "I don't have it yet"));
```

Finally, the `lastName` get's processed, and now you have nowhere in memory with `firstName`, so you'll end up doing this to make the second call with the `lastName`.

```js
console.log(printName('I lost it', 'Doe'));
```

I know, the example is kinda stupid, but the idea is to make two function calls and relate their local variables together. This is possible with closures.

### Now what are closures?

In Eloquent JavaScript it says

> …being able to reference a specific instance of a local binding in an enclosing scope is called closure.

Simply put, closures are functions that have access to the scope of outer functions even when the outer functions have closed (no longer active).

That means a child function can make use of any local variable declared in a parent function anytime, even after the parent function has been called and is no longer active.

The way it works is this, when we create a function with any local variable, the function returns another function (which is a child function), and as stated the child function has access to the variable in the parent function.

So when the function is called, the value is a function, which can be called. i.e

```js
function callMe() {
  return () => 'Hello world';
}

const funcVal = callMe();
console.log(funcVal());
```

This is just an expression of "functions are what they return", or better expressed as "function as values".

So when a function that returns a string is called, the properties and methods of strings can be used on that function call. Same thing with numbers, arrays, objects, functions.

In this case, our function is returning a function, which means the value of the `callMe()` function can be called, because it is a function (you can add params and args).

This is where it gets more interesting...

```js
function callMe(val) {
  return (newVal) => val + newVal;
}

const funcVal = callMe(2);
console.log(funcVal(2)); // 4
```

We've called the `callMe()` function once and passed in a value. Now, this value can be used when we call the function it returns. This is closure.

I can call `funcVal()` different times now and it will still have access to the local variable (`val`) of the parent function (`callMe`)

```js
console.log(funcVal(3)); // 5 i.e 2 + 3
console.log(funcVal(10)); // 12 i.e 2 + 10
// we can go on and on
```

Now the rule of local variables of a function not trampling themselves on different calls still STANDS, we've only made one call to the parent function `callMe`, let's try calling it one more time

```js
const funcVal = callMe(2);
const funcVal2 = callMe(100); // local variable (val) will be created anew here with a value of 100.

console.log(funcVal(2)); // 4 i.e 2 + 2
console.log(funcVal2(10)); // 110 i.e 100 + 10
```

So basically, it's the function they return that does the magic. Even at that, their local variables are still not trampling each other on different calls

```js
console.log(funcVal(3)); // 5 i.e 2 + 3
console.log(funcVal(10)); // local variable (newVal) will be created anew here, but it still has access to the local variables in the outer function. so we get 12 i.e 2 + 10
```

Now let's head back to our initial examples or problems. Let's first solve the name issue.

Recall we had a function `printName` to print a user's first name and last name, but for some reason, the last name will be delayed (and we know about this) so we have to go on without it initially. And then when it finally comes we should print the full name. This is what we'd do

```js
function printName(firstName) {
  return (lastName) => `${firstName} ${lastName}`;
}
```

Now the function has changed a little bit

- the function `printName` now takes just one argument (`firstName` - the guy we know will not be delayed)
- it (`printName`) now returns a function instead of returning a string.
- and the child function takes `lastName` (the guy which we know will be delayed) and then returns the full name string.

If we try logging, it will now make more sense

```js
// first name comes
const user = printName('John');
// after a while, last name comes
console.log(user('Doe')); // John Doe
```

Voila!! Problem solved using closures. Let's add another user

```js
// first name comes
const user = printName('John');
// after a while, last name comes
console.log(user('Doe')); // John Doe

// new user
const user2 = printName('Sarah');
console.log(user2('Michelle')); // Sarah Michelle
```

I know there are tons of other ways to solve this, but this is yet another way.

Now the last example before we call this article a wrap - our counter.

Recall we had a function `incrementFrom` which is in no way incrementing. How do we solve this?

```js
function incrementFrom(count) {
  return () => count++;
}
```

Just one thing changed, we returned a function that returns `count + 1` instead of returning just `count + 1`.

Now let's see if it works

```js
const addOne = incrementFrom(5);
console.log(addOne()); // 5
console.log(addOne()); // 6
console.log(addOne()); // 7
console.log(addOne()); // 8
// and on and on
```

Gladly and unsurprisingly, it works!!

So this is how closures can be very useful in programming.

### Conclusion

Now, this is quite a lot to take in, if it's your first time learning about closures. But when you practice over time it will become clear.

Thanks for reading to the end, I hope you enjoyed and learned from it as I did. I'll see you next time. But in the meantime, you can leave comments for me to let me know what you think. You can also click the like and share button so we can reach more developers.


Let's connect, hit me up on Twitter [@elijahtrillionz](https://twitter.com/elijahtrillionz)