Closures is an important topic when it comes to JS so let's understand it in detail
To understand closure we first need to have a good knowledge of functions and lexical scoping because closure is dependent on these concepts
To start I will explain about functions and higher-order functions so if you already know about it you can directly go to the closure part
Let's start by revising what we know about functions
We know how to call functions
Normally calling a function
function hello(){console.log("hi)}hello()Calling with an object
const obj = {name: "Jett",speed() {console.log("Woos");},};obj.speed();Using the call method
function hello(){console.log("hi)}hello.call()We can also use "new" operator
const four = new Function("num", "return num");four(4);
We know that whenever a function is invoked it creates an execution context and that execution context consists of the "this" keyword, the "arguments" keyword, and finally the variable environment, if you want to know more about execution context do refer my article here
A important thing to know about functions is that
Functions are a special type of object which are called callable objects in JS
Why you ask, because if you imagine a box with the name of the function and some code with three properties - call, apply and bind
If you now go to your text editor and type a function and then see by the intelligence you will see something like this
It will give you the options or the methods that you can use with the function itself so that means it behaves like an object
Now how does this concern us, well if functions are objects then we can store them around like objects, move them and pass them to another function
Functions are first-class citizens in JS
What does that means is
1.We can store a function into a variable
var greet = function () {};
2.We can pass a function as an argument to another function
function greet(fn) {fn();}greet(function () {console.log("hello");});
- We can also return a function
function greet() {return function () {console.log("Hi there");};}greet()();
So basically what I am trying to say is function can do pretty much everything other data types can do in JS
Higher-Order Functions (HOF)
A HOF is a function that can return a function or a function that can take function as an arguments
And we also know about the lexical scoping in JS and if you don't, then you can refer to here article but in brief,
Lexical scoping or static scoping means the available variable data and functions are dependent on where the function is defined and with that we are now ready to understand the concept of closures
Closures
function hello() {console.log("greetings");var a = "hi";return function b() {var b = "hi there";return function c() {var c = "hello there";return `${a} ${b} ${c}`;};};}hello()()(); // hi hi there hello there
Let's understand how the code works here
Well as you guys know that when function hello is called it is pushed onto the stack and after it returns another function it gets popped off the stack (and if a function gets popped off the stack then their variable environment gets destroyed) then function b comes onto the stack return a function and again pops off the stack
Finally, function c comes and return all the variables that were defined while in function hello and b
But didn't I just said that those functions were popped off the stack , so how does function c has the values of the variables inside it , because as we know that when ever the function gets popped off the stack we don't have access to their variable environment
And this concept of accessing the variables even if the function is popped off the stack which returning a function is called closure
Let dive deep
What happens is that the first time when the JS engine parses the code it sees that the variables are mentioned even inside the function which is being returned and so because of that it just stores the variable into a contained called closure and when the function is being called which other functions are returned then when the JS engine gets the values of the varialbes from the closure box and returns the function
Now this allows us to have uses such as memory efficiency and encapsulation
Let's see how things can be memory efficient
Memory efficient
function work(index) {const arr = new Array(7000).fill(":)"); // create new array with 7000 elements of :)console.log("created");return arr[index];}work(55); // createdwork(99); //createdwork(800); // created :)
Here we have created a function that creates a new Array of 7000 elements with ":)" and finally return it, well this process takes time if we call it again and again and is not very memory efficient
What we can do instead is
function work(index) {const arr = new Array(7000).fill(":)"); // create new array with 7000 elements of :)console.log("created");return function () {return arr[index];};}const workHard = work();workHard();workHard();workHard();// created :)
Here even if we call the workHard function many times it will give only one output and that saves a lot of time and memory instead of creating an array every single time on invocation
Encapsulation
Also with the help of closure, we can use encapsulation which means is a process of bundling the data (i.e. variables) and methods within the functions
We have covered the most part here , but if you think we missed something or we have some error then please suggest and give us feedback, we would love to know your thoughts and ideas
Was the article helpful? Do you have any doubts? Any topic you would like us to cover?
Reach out to us on Twitter and Instagram where we try to provide more value in threads and carousal formats
Thank You for your time
Keep learning, Keep coding :)