- The closure is when a function is able to remember and access its lexical scope even when that function is executing outside its lexical scope. It is all around in JS. Usually when functions are passed around as values and executed at different locations in the code are all examples of Closure.
// A simple example where bar has access to the lexical scope of foo even
// though it is executed in outer scope, this is because bar() has closure
// over the lexical scope of foo().
function foo() {
var a = 2;
function bar() {
console.log(a);
}
bar();
}
foo(); // 2
// A little more specific example which puts full light on closure. The function
// foo() returns function object, which we assign to a variable
// baz and then we execute it
function foo() {
var a = 2;
function bar() {
console.log(a);
}
return bar;
}
// Here we execute a reference copy of foo, not foo itself and we still
// get the lexical access
var baz = foo();
baz(); // 2
// An example where we are indirectly passing around the function as value and
// executing it at a different place in code
var fn;
function foo() {
var a = 2;
function baz() {
console.log( a );
}
fn = baz; // assign baz to global variable
}
function bar() {
fn();
}
foo();
bar(); // 2
- Below given example illustrates closure in loops
for (var i=1; i<=5; i++) {
setTimeout( function timer() {
console.log(i);
}, i*1000);
}
// While it is tempting to think that this code will output 1, 2, ..5 with
// one second interval but this is not true, the output will be 6, 6, 6, .. 6
// five times with one second interval each.
// Explanation: The terminating condition of loop is when i is 6, thus 6 is
// printed, at each iteration the loop doesn't have its own copy of i, but the
// global i instead which is accessed by each iteration. They are closed over
// the same shared global scope.
// We can use IIFE to fix this issue, because we can create a new scope for each
// iteration using IIFE
for(var i=1; i<=5; i++) {
(function() {
var j = i;
setTimeout( function timer() {
console.log(j);
}, j*1000);
})();
}
// Best solution is to use let
for(var i=1; i<=5; i++) {
let j=i;
setTimeout( function timer() {
console.log(j);
}, j*1000);
}
// A exceptional short form is also available to use `let` directly in the
// head of for loop
for(let i=1; i<=5; i++) {
setTimeout( function timer() {
console.log(i);
}, i*1000);
}
- Further closure can be experienced in the modules also as given in the example below, we will create a function in a file and execute that function in another file and eventually we will observe that the scope is still accessible.
*bar.js*
function hello(who) {
return "Hi " + who;
}
export hello;
*foo.js*
// import only `hello()` from "bar" module
import hello from "bar";
var hungry = "hippo";
function capitalize() {
console.log(hello(hungry).toUpperCase());
}
export capitalize;
*baz.js*
// import the entire "foo" and "bar" modules
**module foo from "foo";
module bar from "bar";
console.log(bar.hello("rhino")); // Hi rhino
foo.capitalize(); // HI HIPPO