DEV Community

Cover image for Closures in JavaScript

Closures in JavaScript

Parwinder πŸ‘¨πŸ»β€πŸ’» on July 12, 2020

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. ...
Collapse
 
wulymammoth profile image
David • Edited

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...

Collapse
 
bhagatparwinder profile image
Parwinder πŸ‘¨πŸ»β€πŸ’»

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!

Collapse
 
wulymammoth profile image
David • Edited

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); 
}
Thread Thread
 
bhagatparwinder profile image
Parwinder πŸ‘¨πŸ»β€πŸ’»

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

Collapse
 
endeavor42 profile image
Fitri

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.

Collapse
 
bhagatparwinder profile image
Parwinder πŸ‘¨πŸ»β€πŸ’»

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.

Collapse
 
endeavor42 profile image
Fitri

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

Collapse
 
karlstens profile image
Karlstens

I see lots of similarities between closures and classes. Here is another car speed example, but using classes. I'm not saying that the two are completely interchangeable for all things, but I'd like to know where it's better to leverage closures instead of classes, or even, employ a mixture of both techniques together?

class CarMonitor {
    constructor() {
      this.speed = 0;
    }

    accelerate() {
      return ++this.speed;
    }

    decelerate() {
        return --this.speed;
      }
  }

  const car = new CarMonitor();
  console.log(car.speed) // 0
  console.log(car.accelerate()); // 1
  console.log(car.accelerate()); // 2
  console.log(car.decelerate()); // 1
  console.log(car.accelerate()); // 2
  console.log(car.accelerate()); // 3
  console.log(car.speed) // 3
  console.log(car.decelerate()); // 2
  console.log(car.decelerate()); // 1
  console.log(car.decelerate()); // 0
  console.log(car.speed) // 0

Enter fullscreen mode Exit fullscreen mode
Collapse
 
karlstens profile image
Karlstens

Ok, I had an idea where we could employ a closure to break past the "Max" speed of a given vehicle via a logarithmic curve. ... It's probably best that I step away from the keyboard...

class CarMonitor {
    constructor({ maximumSpeed = 4, modelName = "clunker" } = {}) {
      this.speed = 0;
      this.maximumSpeed = maximumSpeed;
      this.modelName = modelName;
      this.boostFactor = 0;

      // Define the closure
      this.accelerateBeyondMax = (() => {
        let boost = 0;

        return () => {
          boost += 0.01; // Increment boost
          return Math.log(boost + 1); // Logarithmic curve
        };
      })();
    }

    accelerate() {
        if (this.speed < this.maximumSpeed) {
          return ++this.speed;
        } else {
          this.boostFactor = this.accelerateBeyondMax();
          // Calculate the new speed with the boost factor, then round it to the nearest integer
          const newSpeed = Math.round(this.maximumSpeed + this.boostFactor);
          // If the new speed is greater than the current speed, update the speed
          if (newSpeed > this.speed) {
            this.speed = newSpeed;
          }
          return this.speed;
        }
      }


    decelerate() {
      return this.speed = this.speed > 0 ? this.speed - 1 : 0;
    }
  }

  const car = new CarMonitor({ maximumSpeed: 10, modelName: "Fast Car" }); 
  console.log(car.modelName); // "Fast Car"
  console.log(car.speed); // 0
  console.log(car.accelerate()); // 1
  console.log(car.accelerate()); // 2
  console.log(car.decelerate()); // 1

  // Accelerate to maximum speed
  for (let i = 0; i < 9; i++) {
    console.log(car.accelerate());
  }

  // Try to accelerate beyond maximum speed
  for (let i = 0; i < 1000; i++) {
    console.log(car.accelerate());
  }

  const myClunkerCar = new CarMonitor({}); 

Enter fullscreen mode Exit fullscreen mode
Collapse
 
deepharshsri1 profile image
deepharshsri

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.

Collapse
 
bhagatparwinder profile image
Parwinder πŸ‘¨πŸ»β€πŸ’»

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).

Collapse
 
umutakyol profile image
asdsadasf

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

Collapse
 
bhagatparwinder profile image
Parwinder πŸ‘¨πŸ»β€πŸ’»

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. πŸ™ŒπŸ½

Collapse
 
jogii profile image
Jogendra

Thanks a lot.. And finally i understand closures.. :P