Javascript Closures

Yeah. Closures. I know I’m kind of late in getting to this. Closures have been widely discussed subject among developers and I feel like I should get a good handle on it myself. First things first though, it’s important to understand that closures in javascript are a feature not a built in construct. If you’ve been writing in Javascript you’ve already encountered closures you just weren’t aware of it. I think the concept of closures tends to intimidate or confuse a lot of beginners and even self proclaimed experts in javascript.

Another important thing to note is that Javascript in ES6 has rolled out with some new constructs to tackle this complexity. Namely, the ‘let’ keyword has allowed for more control over scope and arrow functions which provide a concise approach to writing anonymous functions.

I would like to give credit to TechSith for making it easy to understand closures. I’ve watched and read so many online tutorials and they just don’t seem to explain it very well – if anything I found myself a little more confused each time. So let’s dive in.

Lexical Scope

Lexical scoping in javascript is a term to describe how variables defined outside a function are available in other functions in the program, without even passing them as arguments. Let’s check out a super simple example below:


let i = 1;

const f = () => {
        console.log(i);
}

f(); // output: 1

We see that the variable “i” is never passed to our declared f() function – however when we invoke the function it outputs “1”. How is this possible? Well this is one of Javascript’s really weird parts and if you aren’t paying attention to how you write your programs this can throw you off. Here though – we are already making use of “closures, it’s really hard to notice though.

The key thing to understand is that the “i” variable which contains the value “1” is available everywhere. This is because the “i” variable is attached to the outermost window object.

But things start getting a little tricky when we start moving things around. Let’s check out another case below:


if (true) {
        let i = 1;

        const f = () => {
                console.log(i);
        }

}

f(); // ReferenceError: f is not defined

Yup we get an reference error when the program is run. This is because “i” is defined inside of a block scope, in this case inside an if statement. So how do we fix this? Well, we can define the function f() outside the if conditional


let f;
  
if (true) {
        let i = 1;

        f = () => {
                console.log(i);
        }

}

f(); // outputs 1

Okay so by initially declaring “f” outside the if block, we’ve made the “i” variable available within the if scope. Being lexically aware where our functions and variables are declared has really helped me keep track and avoid any confusion when writing out my programs.

Okay so far we’ve been attaching everything to the global scope, so a closure is hardly noticeable. Let’s make this more obvious by working inside a function declaration. See the example below.


for (let i = 0; i < 3; i++) {

        setTimeout(
                () => {
                        console.log(i);
                }, 1000);

}

// outputs 0, 1, 2

Well that’s great thanks to the ‘let’ keyword introduced in ES6. But what if we changed ‘let’ to ‘var’, we get a different result.


for (var i = 0; i < 3; i++) {

        setTimeout(
                () => {
                        console.log(i);
                }, 1000);

}

// outputs 3, 3, 3

Okay so we aren’t getting the result we expected. Why though? It shouldn’t even be going up to the 3 at all.

Well the answer to this lies in how the var keyword behaves compared to the let keyword. The var keyword is functionally scope whereas the let keyword is block scoped. And so when we use var in a loop construct, a new variable isn’t created, it’s the value that changes. However, when using the let variable, each variable AND value is updated and so we get three separate values for “i”

I hope this post has helped you better understand closures, I know it has for me. Reviewing lexical scope, block scope and function scope can additionally help you understand how closures work. I’ll cover these topics in a future post. Until then, keep immersing yourself into anything you want to learn!