A closure is the combination of a function and the lexical environment within which that function was declared.
OR
A closure in JavaScript is a function that remembers its lexical scope (the scope in which it was created) even when it is executed outside that scope. This means that a function can "remember" the environment in which it was created, including any variables that were in scope at the time.
Imagine you want to speed posting some documents. You put all the documents in an envelope and sealed it. All the documents are inside the envelope and after speed posting from one place to another, the documents are still inside it. Similarly, a closure "remembers" the variables from the place where it was created, even after it moves to a different place in the code.
Lexical Scoping:
JavaScript functions are lexically scoped, meaning that they can access variables from the surrounding code in which they were defined, even if that surrounding code is no longer in execution.
In simple terms:
- Lexical scope means that the scope of a variable is defined by where it is written in the code, not where it is called from.
- A closure captures variables from the outer scope at the time the function is created, not when it is called.
How does closure work?
In a closure, when a function is declared inside another function, it gets access to the outer function's variables. Even after the outer function has finished executing, the inner function retains access to those variables.
1: Basic Closure Example:
function outerFunction() {
let outerVariable = 'I am outside!';
function innerFunction() {
console.log(outerVariable); // Inner function uses outerVariable
}
return innerFunction;
}
const closureFunc = outerFunction(); // Call the outer function
closureFunc(); // Even though outerFunction is done, innerFunction can still access outerVariable
// Output: I am outside!
Explanation:
-
outerFunction
has a variable calledouterVariable
and it returns another function (calledinnerFunction
). - When
outerFunction()
is called, it returns theinnerFunction
. - Even though
outerFunction
has finished executing by the timeclosureFunc
is called, theinnerFunction
still remembers theouterVariable
that was declared in the outer function. This is theclosure
.
2: Closure with Arguments:
function multiplier(x) {
return function(y) {
return x * y;
}
}
const multiplyBy = multiplier(5);
console.log(multiplyBy(10)); // Output: 50
console.log(multiplyBy(6)); // Output: 30
Explanation:
-
multiplier
is a function that takes a parameter x and returns an inner function. - The inner function takes a parameter y and returns the multiply of x and y.
- When we call
multiplier(5)
, it returns a new function that multiplies 5 by any number passed into it. - Each call to
multiplyBy
remembers x = 5 (from the outer scope) and can access it to calculate the result.
3: Closures and SetTimeout
function createCounter() {
let count = 0;
function innerCounter() {
count++;
console.log(count);
}
return innerCounter;
}
const counter = createCounter();
counter(); // Immedietely Output 1
setTimeout(counter, 1000); // Output after 1 second: 2
setTimeout(counter, 2000); // Output after 2 seconds: 3
setTimeout(counter, 3000); // Output after 3 seconds: 4
Explanation:
- The function
createCounter
defines a variablecount
and returns a functioninnerCounter
that incrementscount
each time it is called. - Even though
createCounter
has finished executing, the returned function (the closure) still "remembers"count
. - The
setTimeout
calls invoke the returned function, and the counter continues to increment as expected.
4: Practical Use of Closure (Data Encapsulation)
function Counter() {
let count = 0; // private variable
this.increment = function() {
count++;
console.log(count);
}
this.decrement = function() {
count--;
console.log(count);
}
this.getCount = function() {
return count;
}
}
const myCounter = new Counter();
myCounter.increment(); // Output: 1
myCounter.increment(); // Output: 2
myCounter.decrement(); // Output: 1
console.log(myCounter.getCount()); // Output: 1
Explanation:
- The
Counter
function acts as a constructor, defining a private variablecount
. - The methods
increment
,decrement
, andgetCount
form closures because they have access to thecount
variable. - Outside code cannot directly access or modify
count
; it can only interact with it via the provided methods.
Why Closures are Important?
- Data Privacy/Encapsulation: You can create private variables that cannot be accessed directly from the outside world, but can still be manipulated via functions (like in the Counter example above).
- Function Factories: Closures allow you to create customized versions of a function, like the makeAdder example.
- Asynchronous Programming: Closures are useful in scenarios like asynchronous code, where functions need to "remember" their environment (like the setTimeout example).
Performance Consideration
While closures are a powerful tool, they come with some potential performance implications:
- Memory Usage: A closure can keep a reference to its outer variables, potentially leading to higher memory usage. If you create many closures that capture large amounts of data, it could slow down the application.
- Garbage Collection: Closures may delay the garbage collection of variables they capture. If you no longer need the closure, ensure you release references to it.
Key Concept:
- A closure is created when a function has access to its parent function’s variables, even after the parent function has finished executing.
- The inner function "closes over" the variables from the outer function, which is why it’s called a closure.
Top comments (1)
Nice