Closures — The way encapsulation and Module patterns work in JavaScript

Jainishshah
4 min readJun 18, 2021

Hola Js Devs, I am sure you would have heard the term “closures” during development or even during interviews. But are you sure you know closures thoroughly? Even if you haven’t this article will help you understand it in detail. I guarantee that you might have used closures without even knowing (this happened in my case) and you’ll have a eureka moment after realizing you have been using it all along be it in react/angular/node etc. Well, Buckle up boys and girls.

The general definition of closures goes as “Closure is when a function can remember and access its lexical scope even when that function
is executing outside its lexical scope.”

And your reaction would be something like……

Well, chill out !!!! Let’s break down this statement and try to understand what it says.

  1. Access/Remember Lexical scope
  2. Function executing outside its lexical scope

Too many jargons aren't they. Let’s take an example then

function doSomething(){
var a = 5;

function printIt(){
console.log(a);
}
return printIt;}var closureFn = doSomething();closureFn();

What do you think the output of the above code will be?

Your initial guess would be it’s “undefined”. But why? When you execute doSomething() it returns some value and the function execution is done. Since this function is not used anywhere else, the natural thing the “engine” would do is to invoke the garbage collector and free up the memory. So the entire scope of doSomething() should be deleted. And when the closureFn() is called it shouldn’t get any value of “a” and print “undefined” in “non-strict” mode.

Sadly you are wrong, the output will be “5”. But why is that? Well, it's the magic of “closure”.

Let’s talk a bit about “lexical scope” I have already explained about lexical scope here, please go through it if you are unfamiliar with it. When the engine first compiled and executed the doSomething function it created the scope of that function which looks like

Scope: [ (doSomething) => { a : 5, printIt: pointer to the fn's location in the heap}]->[(globalScope) => {}]

Our doSometing function also declares a function printIt. It is stored in the heap somewhere and a reference to it is linked in the scope. The reference to printIt is returned and stored in the closureFn. As you would expect the garbage collector comes into the picture but how would it decide whether free up the memory used by doSomething ? Well the algorithm that works in the background works something like this

Find “live” objects and free up all the dead memory from the heap. The objects that have been instantiated are linked to other objects if they are connected (In this case Scope). All the objects that are linked are known as live objects and are preserved and the rest of the heap is cleared. Since the “printIt” is returned its scope is linked to “doSomething’s” scope and it's a live object. In this way, the variables and functions in the scope of doSomething are still preserved. Not only of doSoemthing’s but also of all the other objects that may have been chained together. eg

function grandParentDoSomething(){  
var a =5
function parentDoSomething(){
var b= 10;
function doSomething(){
var c = 15;
function printIt() {
console.log("HEY")
}
return printIt;
}
doSomething()
}
parentDoSomething();
}
var chainedClosure = grandParentDoSomething();/* Here all the variables a, b, c are preserved*/

I hope you get the gist of how it works.

Let's talk about what are some of the use cases

  1. Module Pattern in JS ( modules are created using this)
  2. Security Purpose ( Just like protected keywords in Java)

Well all good things do come with some side-effect

The biggest disadvantage of Closures is “High Memory Consumption”. If you implement closure incorrectly you have sure shot to over-utilize the available memory and your application will crash.

I guess by now you have understood what closures are and how they work. Let’s take few examples and determine what the output will be.

function wallet(){

var currentAmount = 1000;
function deposit(amt){
currentAmount += amt;
}

function withdrawal(amt){
currentAmount -= amt;
}
function currentBalance(){ return currentAmount;
}
return {deposit, withdrawal, currentBalance };};var walletA = wallet();
console.log(walletA.currentBalance()); // 1000
walletA.deposit(50);
walletA.withdrawal(200);
console.log(walletA.currentBalance()); // 850

You can get an idea of how the protected variables are implemented in JS using closures. ( Note: this code is just for demo purposes, you will have to write some more logic to check only integer values are passed and some error handling also if the data that is returned is non-primitive ie an object always send a copy destructuring it i.e. use spread operator. Objects are returned by reference so if you don’t send a copy you are violating the integrity of the data).

Let’s take one more example and it's a common interview question.

function() print{    for(var i=1;i<=6;i++){
setTimeout( function(){ console.log(i); }, 1000 );
}
}

Try to solve it yourself and answer it in the response. Will be publishing the answer next week.

Hope you enjoyed reading the article and learned something. Feel free to reach out to me via email jainishshah12@gmail.com

LinkedIn https://www.linkedin.com/in/jainish-shah-223137121/

--

--