loading...
Cover image for Closures in JavaScript

Closures in JavaScript

bhagatparwinder profile image Parwinder πŸ‘¨πŸ»β€πŸ’» ・4 min read

JavaScript with Parwinder (42 Part Series)

1) What is Javascript? 2) Where can I run JavaScript? 3 ... 40 3) Types in JavaScript 4) What are objects in JavaScript? 5) Reference vs Value 6) Arrays in JavaScript 7) Array Methods in JS 8) Array Instance Methods 9) Array Instance Methods II 10) Functions in JavaScript 11) Type of Functions in JavaScript 12) Scope in JavaScript 13) Hoisting in JavaScript 14) Closures in JavaScript 15) Debugging in JavaScript: Console Methods 16) The new Keyword in JavaScript 17) JavaScript: Type Conversion 18) JavaScript: While Loop 19) JavaScript: Do While Loop 20) JavaScript: For Loop 21) Lesser-Known JavaScript Tricks 22) Events & Event Handling 23) Event Bubbling 24) Event Capturing 25) JS: Switch Statement Explained 26) Arrow Function: Basics 27) this Keyword in JavaScript 28) Arrow functions & this keyword 29) Callback Functions & Callback Hell 30) Strict Mode in JavaScript 31) Optional Chaining 32) Timers & Intervals 33) Arrow Function and The new, arguments & super Keyword! 34) Promises: Introduction 35) Promises: Chaining, Error Handling & Operators 36) New Promise Methods: allSettled & any 37) Async/Await: Introduction 38) Async/Await: Common Mistakes 39) Async/Await: Error Handling 40) Truly Understand Bind, Call & Apply 41) Destructuring Arrays 42) 8 Features From ES2020/ES11 You Should Know!

What are closures?

I consider closures in JavaScript as an advanced topic. It is one of the topics that gets asked in interviews a lot.
Understanding closures would be more comfortable if you have read my previous blog posts or know about scope in JavaScript.

Function scope in JavaScript means that a variable declared inside a function is only accessible in that function. It is, however, accessible to the function and any of its child functions. Closures take this one step further. Closure makes sure that the parent scope is available to the child function even after the parent function has finished execution.

Example

function outer() {
    const outerVariable = "outer";
    function inner() {
        const innerVariable = "inner";
        console.log(`${outerVariable} ${innerVariable}`); // outer inner
    }
    inner();
}

outer();

I created and executed the outer function above. This action creates and invokes inner function. inner function successfully logs the variable it has declared and the variable in the parent function. It is expected since the child function has access to the parent function's scope.

Now let us not call the inner function and instead return it.

function outer() {
    const outerVariable = "outer";
    function inner() {
        const innerVariable = "inner";
        return (`${outerVariable} ${innerVariable}`);
    }
    return inner;
}

const returnFunction = outer();
console.log(returnFunction); // Function returnFunction

Variable returnFunction is a function because that is what was returned by outer. No surprises.

🚨At this point, execution of outer function has finished, and the return value has been assigned to a new variable.

This is key. JavaScript garbage collection should have removed all traces of outerVariable because that is what happens when a function is popped off the stack and finished executing. Let us run returnFunction.

function outer() {
    const outerVariable = "outer";
    function inner() {
        const innerVariable = "inner";
        return (`${outerVariable} ${innerVariable}`); // outer inner
    }
    return inner;
}

const returnFunction = outer();
console.log(returnFunction); // Function returnFunction
console.log(returnFunction()); // outer inner

Surprise surprise! It can still log the value of a variable in its parent function (which has finished executing).

JavaScript garbage collection does not clear out variables of a function if that function has child functions returned. These child functions can run later and are fully eligible to access the parent's scope by the principal of lexical scoping.

This behavior of garbage collection is not only limited to child function. A variable is not garbage collected as long as anything maintains a reference to it.

Real World Example

Let's say that I am programming about a car. This car can accelerate like a real-world car, and whenever it does accelerate, the speed of the car increases.

function carMonitor() {
    var speed = 0;

    return {
        accelerate: function () {
            return speed++;
        }
    }
}

var car = new carMonitor();
console.log(car.accelerate()); // 0
console.log(car.accelerate()); // 1
console.log(car.accelerate()); // 2
console.log(car.accelerate()); // 3
console.log(car.accelerate()); // 4

You can see how the speed of the car is provided by carMonitor, and it is accessible by accelerate function. Every time I call accelerate, not only can it access the said variable but increment it from last value and return it.

Creating Private Variables using Closures

Let's take the example for carMonitor.

function carMonitor() {
    var speed = 0;

    return {
        accelerate: function () {
            return speed++;
        }
    }
}

var car = new carMonitor();
console.log(car.accelerate()); // 0
console.log(car.accelerate()); // 1
console.log(car.accelerate()); // 2
console.log(car.accelerate()); // 3
console.log(car.accelerate()); // 4
console.log(speed); // speed is not defined

You can see that the variable is a private variable for function carMonitor, and it is only accessible by the child function accelerate. No one outside can access it. One could argue that it is expected due to the function scope. It is private to carMonitor, and it is private to each new instance of carMonitor.

Every instance maintains its copy and increments it!

This should help you realize the power of closures!

function carMonitor() {
    var speed = 0;

    return {
        accelerate: function () {
            return speed++;
        }
    }
}

var car = new carMonitor();
var redCar = new carMonitor()
console.log(car.accelerate()); // 0
console.log(car.accelerate()); // 1
console.log(redCar.accelerate()); // 0
console.log(redCar.accelerate()); // 1
console.log(car.accelerate()); // 2
console.log(redCar.accelerate()); // 2
console.log(speed); // speed is not defined

car and redCar maintain their own private speed variables, and speed is not accessible outside.

We are enforcing the consumer to use the methods defined on the function or class rather than accessing the properties directly (which they should not). This is how you would encapsulate your code.

I hope the article cleared up any doubts about closures in JavaScript!

Common Interview Question

Here is an interview question about closures that is asked fairly frequently.

What do you think the output of the following piece of code will be:

for (var i = 0; i <= 5; i++) {
    setTimeout(function () {
        console.log(i);
    }, 1000);
}

If you guessed numbers from 0 to 5 with a one-second gap, you are in for a surprise. By the time 1 second has passed for the setTimeout, i has a value of 6 at the time of invocation! We would like to use the value of i from time of creation and IIFE + closures will help you do that.

for (var i = 0; i <= 5; i++) {
    (function (i) {
        setTimeout(function () {
            console.log(i);
        }, 1000);
    })(i);
}

There is another way to solve this issue. Using the let keyword.

var in our loop used to declare i creates a function scope. This results in one binding shared for all loop iterations. When the six timers complete, they all use the same variable with a finally settled value of 6.

let has block scope and when used with a for loop creates a new binding for each iteration of the loop. Each timer in the loop gets a different variable with a different value from 0 to 5.

for (let i = 0; i <= 5; i++) {
    setTimeout(function () {
        console.log(i);
    }, 1000);
}

And now the output will be numbers from 0 to 5. If you are targeting ES5, use the IIFE plus closures method. If you have access to ES6, use the let keyword method.

This is what Babel does under the hood when it transpiles ES6 code to ES5. It transsforms the above let based code to a combination of closure + IIFE!

JavaScript with Parwinder (42 Part Series)

1) What is Javascript? 2) Where can I run JavaScript? 3 ... 40 3) Types in JavaScript 4) What are objects in JavaScript? 5) Reference vs Value 6) Arrays in JavaScript 7) Array Methods in JS 8) Array Instance Methods 9) Array Instance Methods II 10) Functions in JavaScript 11) Type of Functions in JavaScript 12) Scope in JavaScript 13) Hoisting in JavaScript 14) Closures in JavaScript 15) Debugging in JavaScript: Console Methods 16) The new Keyword in JavaScript 17) JavaScript: Type Conversion 18) JavaScript: While Loop 19) JavaScript: Do While Loop 20) JavaScript: For Loop 21) Lesser-Known JavaScript Tricks 22) Events & Event Handling 23) Event Bubbling 24) Event Capturing 25) JS: Switch Statement Explained 26) Arrow Function: Basics 27) this Keyword in JavaScript 28) Arrow functions & this keyword 29) Callback Functions & Callback Hell 30) Strict Mode in JavaScript 31) Optional Chaining 32) Timers & Intervals 33) Arrow Function and The new, arguments & super Keyword! 34) Promises: Introduction 35) Promises: Chaining, Error Handling & Operators 36) New Promise Methods: allSettled & any 37) Async/Await: Introduction 38) Async/Await: Common Mistakes 39) Async/Await: Error Handling 40) Truly Understand Bind, Call & Apply 41) Destructuring Arrays 42) 8 Features From ES2020/ES11 You Should Know!

Posted on by:

bhagatparwinder profile

Parwinder πŸ‘¨πŸ»β€πŸ’»

@bhagatparwinder

Happy, cheerful, confident. Web developer based out of Chicago :)

Discussion

markdown guide
 

Awesome post, Parwinder!

One clarification that I think is worth mentioning. My understanding is that the variable declared and initialized is not GC'd because there's a child function. I think it's actually simpler than that, right? It isn't GC'd so long as ANYTHING maintains a reference to it, whether it's a function, another variable, some object's state, etc

let bar = null;

(function outer() {
  const foo = "foo";
  bar = foo;
})(); // function has returned

console.log(bar); // still has access to foo
foo

Absolutely love the last example! I feel like people don't really know this and this is ideally how encapsulation is done. However, it may be worth mentioning that it's usefulness or utility comes from being able to enforce an interface -- forcing the consumer of the API to use the methods defined on the class instead of accessing properties/fields that aren't supposed to be accessible just because they can...

 

David, great feedback ❀️

The more I write, the better writer I am becoming (or at least I think I am).

Then I see feedback like yours, and I am surprised that I could have presented things in a lot better way. I will update the post to reflect your feedback!

 

You're doing great. I always miss a lot of things and love learning about what I've forgotten or don't yet know (there's a lot) :D

When I used to work in JavaScript and interview folks, I typically asked this question that is also solved with the use of a closure:

// what does the following return?
// unfortunately, after a second passes, we get a trail of sixes,
// because the function looks at `i` at invocation time
for (var i = 0; i <= 5; i++) {
  setTimeout(function() {
    console.log(i);
  }, 1000);
}

// we can "fix" this by employing an immediately-invoked function expression
// with closure maintaining the state of i at the time of construction
// the following will now print 0, 1, 2, 3, 4, 5
for (var i = 0; i <= 5; i++) {
  (function(i) {
    setTimeout(function() {
      console.log(i);
    }, 1000);
  })(i); 
}

Haha, I am glad that you have mentioned this example. This is a go-to question for me too.

 

Hi, great article!
I really liked your last example and decided to code it myself. After executing it, I got a bunch of sixes, as expected. However, when I declared the variable i using the β€œlet” keyword, I got 0-5. Why is that happening? I thought i has a value of 6 at the time of invocation.

 

Thanks for reading and bringing up the confusion. This happened due to my copy paste job from my notes. I have corrected it to var.

As a bonus, I have updated the blog post with an explanation of why let solves this problem as well (similar to closure + IIFE). This should answer the question you have πŸ˜… If you still have any more questions please feel free to follow up.

 

Thank you for the quick response! Your explanation answered my question πŸ˜„

 

Sir I think the example you have given for "for loop using let" will give correct answer as 0,1,... because "let is block scope" and we don't want to use IIFE at that time but if we use "var " there in for then actually we need IIFE so that we can get 0,1,... as answer bcz var is function scoped.

 

Thanks for bringing it up. This confusion happened due to my copy paste job from my notes. I have corrected it to var. As a bonus, I have updated the blog post with explanation on why let solves this problem as well (similar to closure + IIFE).

 

Awesome part of JS for interviews, and also useless part of real life apps. There are better, much clever, and simpler solutions. I think. ;)

 

Thanks for the feedback. Clever examples are not always easy to understand. My aim is to make it easy for a beginner to grasp the idea of encapsulation and interface implementation.

That being said, I would welcome any examples you might have that are easy and cleaver. πŸ™ŒπŸ½