Learning JavaScript Closure
As a developer who has always been focusing on Backend development, Closure in JavaScript is not a very familiar concept for me. In this article I will explain simply about Closure based on how I understood using simple examples.
What is Closure?
According to MDN, a Closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function's scope from an inner function. In JavaScript, closures are created every time a function is created, at function creation time.
I simply understand that Closure is a feature where an inner function has access to the outer (enclosing) function’s variables — a scope chain.
The closure has three scope chains:
it has access to its own scope — variables defined between its curly brackets
it has access to the outer function’s variables
it has access to the global variables
Let's look at an example of a closure:
const outerFunc = () => {
let a = 10;
console.log(‘outer func variable’);
const innerFunc = () => {
let b = 5;
console.log(‘inner func variable’);
console.log(‘a+b ‘, a+b);
}
return innerFunc;
}
const result = outerFunc();
console.log(result);
result();
Let’s break down what is happening.
In this revision outerFunc returns innerFunc instead of calling it.
Therefore when the result is set equal to outerFunc, the console statement inside outFunc is logged, but not the statement inside the innerFunc.
innerFunc is not called into action yet. Instead it’s returned and held into result.
In addition, we need to realize that outerFunc has closed after it’s called.
The line with console.log(result) should show that the result now holds the anonymous function value that was innerFunc. Now, when we call result(), we are calling the anonymous function that was assigned to innerFunc.
As a child of outerFunction, this anonymous function has access to the “a” variable inside outerFunction even after it has closed! The closure we created now allows us to continue using the value of the “a” variable every time we call result().
I would like to share more about Closure using some use cases that I learned from other sources.
What can I do with closures in JavaScript?
Immediately-invoked Function Expression (IIFE)
This is a technique that was used a lot in the ES5 days to implement the "module" design pattern (before this was natively supported). The idea is to "wrap" your module in a function that is immediately executed.
(function(arg1, arg2){
...
...
})(arg1, arg2)
This lets you use private variables that can only be used by the module itself within the function – that is, it's allowed to emulate the access modifiers.
const module = (function(){
function func() {
}
const innerVal = "Initial Value";
return {
get: innerVal,
set: function(v) { innerVal = v }
}
})()
var x = module()
x.get() // “Initial Value"
x.set("New value")
x.get() // "New Value"
x.privateValue //Error
Function Factory
Another design pattern implemented thanks to closures is the “Function Factory”. This is when functions create functions or objects, for example, a function that allows you to create user objects.
const createPerson = ({ name, country, hobby }) => ({
name,
country,
hobby,
changeName (name) {
this.name = name;
return this;
},
changeCountry (country) {
this.country = country;
return this;
},
changeHobby (hobby) {
this.hobby = hobby;
return this;
}
});
console.log(createPerson({ name: 'Mike', country: 'USA', hobby: 'sport, music, movie' }));
And using this pattern we can implement an idea from functional programming called currying.
Currying
Currying is a design pattern (and a characteristic of some languages) where a function is immediately evaluated and returns a second function. This pattern allows executing specialization and composition.
Let’s create these "curried" functions using closures, defining and returning the inner function of the closure.
function multiply(a) {
return function (b) {
return function (c) {
return a * b * c
}
}
}
let mc1 = multiply(1);
let mc2 = mc1(2);
let res = mc2(3);
console.log(res);
let res2 = multiply(1)(2)(3);
console.log(res2);
These types of functions take a single value or argument and return another function that also receives an argument. It is a partial application of the arguments. It is also possible to rewrite this example using ES6.
let multiply = (a) => (b) => (c) => {
return a * b * c;
}
let mc1 = multiply(1);
let mc2 = mc1(2);
let res = mc2(3);
console.log(res);
let res2 = multiply(1)(2)(3);
console.log(res2);
Where can we apply currying? In composition, let's say there is a function that creates HTML elements.
function createElement(element){
const el = document.createElement(element)
return function(content) {
return el.textNode = content
}
}
const bold = crearElement('b')
const italic = createElement('i')
const content = 'My content'
const myElement = bold(italic(content)) // <b><i>My content</i></b>
Event Listeners
Another place we can use and apply closures is in event handlers using React.
Suppose we are using a third party library to render the items in our data collection. This library exposes a component called RenderItem that has only one available prop onClick. This prop does not receive any parameters and does not return a value.
Now, in our particular app, we require that when a user clicks on the item the app displays an alert with the item's title. But the onClick event that we have available does not accept arguments – so what can we do? Closures to the rescue:
// Closure
const onItemClick = title => () => alert(`Clicked ${title}`)
return (
<Container>
{items.map(item => {
return (
<RenderItem onClick={onItemClick(item.title)}>
<Title>{item.title}</Title>
</RenderItem>
)
})}
</Container>
)
In this simplified example we create a function that receives the title that display and returns another function that meets the definition of the function that RenderItem receives as a prop.
I hope this will be a little bit helpful to understand about JavaScript Closure.
References:
https://developer.mozilla.org/en-US/docs/Glossary/Closure
https://www.freecodecamp.org/news/closures-in-javascript/