Understanding Closures in JavaScript
Powerful Function Concepts Explained with Examples
Closures are one of the most important Javascript concepts which makes the language more powerful. Closures allow functions to retain access to variables from their parent scope, even after the parent function has finished executing. They provide a powerful way to encapsulate data enabling advanced programming techniques like memoising, currying, implementing higher-order functions, callbacks and more. Before diving further I would recommend you go over the Scope & Scope Chaining concept which will help you understand the closure.
What are Closures?
A closure is a combination of a function enclosed with references to its lexical environment (surrounding state). In Javascript, closures are created every time a function is created.
Consider a function declared inside another function then the inner function forms the Closure with its parent (outer) function. This means that the inner function has access to its scope, its parent function scope and even the global scope.
Why Closures Are Useful?
Data Privacy: Closures allow you to create private variables and functions that are not accessible from the outside. This concept is crucial for building robust and modular code.
Memoisation: Closures enable the creation of function factories that generate customized functions (memoised functions) with preset values. Each generated function retains its own set of variables.
Asynchronous Operations: Closures help preserve the state of variables in asynchronous operations, such as callbacks and event handlers. They ensure that the correct values are available when the function is invoked after any async operation is done.
Due to the closures, Javascript gets the power to implement the code in a more robust way allowing programmers to generate effective design patterns which makes the code more efficient.
Let us understand it with some code examples -
Example Code 1: Creating app counter
function appCounter() {
let count = 0;
return function() {
return ++count;
};
}
const counter = appCounter();
console.log(counter()); // Output: 1
console.log(counter()); // Output: 2
console.log(counter()); // Output: 3
// ...
Here, the appCounter
function creates a closure by returning an inner function. The inner function retains access to the count
variable even after appCounter
has finished executing. Each time counter
is invoked, it increments and returns the count
value.
If we want to make the counter start from a particular value we can pass a parameter to appCounter
function to initialise a counter variable :
function appCounter(start) {
let count = start - 1;
return function() {
return ++count;
};
}
const counter = appCounter(10);
console.log(counter()); // Output: 10
console.log(counter()); // Output: 11
Example 2: Implementing Private Variables
function createPerson(name) {
let username = name;
return {
getName: function() {
return username.toUpperCase();
}
};
}
const person = createPerson('John');
console.log(person.getName()); // Output: JOHN
console.log(person.username); // Output: undefined
// can not access the username variable directly
In this example, the createPerson
function creates a closure by returning an object with a getName
method. The username
variable is only accessible through the getName
method, making it private and encapsulated within the closure.
Summary:
Closures are a powerful concept in Javascript that allows functions to retain access to variables from their parent scope. They provide a way to encapsulate data and behaviour, enabling advanced techniques such as memoising, currying, implementing higher-order functions, callbacks and more. Understanding closures will enhance your Javascript skills and empower you to write more robust and efficient code. Start exploring the world of closures and unlock the true potential of Javascript functions!