what is Closure?
A closure is the combination of a function bundled together with references to its the lexical environment.
function x(){
var a = 7;
function y() {
console.log(a);
}
y();
}
x(); // 7
what about this code,
function x() {
var a = 7;
return function y() {
console.log(a);
};
y();
}
var z = x();
z(); // still 7
because, function x()
returns closure (not only function) so it still remembers the reference to the variable a
, hence logs “7”.
Corner cases
function x() {
var a = 7;
return function y() {
a = 100;
console.log(a);
};
y();
}
var z = x();
z(); // 100
function z() {
var b = 900;
function x() {
var a = 7;
return function y() {
console.log(a, b);
};
y();
}
x();
}
z();
Use cases;
- Module deign pattern
- currying
- functions like once
- memoize
- maintaining state in async world
- setTimeouts
- Iterators
- and many more.
Disadvantages
- more memory consumption
Interview questions
function x() {
var i = 1;
setTimeout(function () {
console.log(i);
}, 1000);
console.log("what logs first");
}
x();
/*
OUTPUT :
what logs first
1
*/
JavaScript does not wait for setTimeout
to expire, it takes closure and attaches a timer with it, and continues the execution. when timer expires, the setTimeout
is executed.
// this program is suppose to print 1 through 5 after each second.
// IT DOES NOT ? WHY ??
function x() {
for (var i = 1; i <= 5; i++) {
setTimeout(function () {
console.log(i);
}, i * 1000);
}
}
what happens in this code ?
- for i: 1 to 5 , a new
setTimeout
is created in memory with the closure of variablei
- all 5
setTimeout
has reference to same variablei
for
loop is executed before anysetTimeout
can even execute so i becomes6
.- then each
setTimeout
executes with the reference value ofi
whose value is now6
.
how do we fix this?
- use let keyword
/*
let is block scope so each setTimeout have seperate copy if i.
*/
function x() {
for (let i = 1; i <= 5; i++) {
setTimeout(function () {
console.log(i);
}, i * 1000);
}
}
- closure of the closure
/*
here each coc() has its own execution context hence each has its onw variable i in memory stage. so it works !!
*/
function x() {
for (var i = 1; i <= 5; i++) {
function coc(x) {
setTimeout(function () {
console.log(x);
}, x * 1000);
}
coc(i);
}
}
data hiding and encapsulation
- data hiding refers to hiding data so that other functions cant access it.
function counter(){
var count = 0;
return function incrementCounter(){
count++;
console.log(count);
}
}
// we can not modify count outside fn counter()
var counter1 = counter();
counter1(); // > 1
counter1(); // > 2
var counter2 = counter();
counter2(); // > 1
counter2(); // > 2
counter2(); // > 3
// as you can see we can create different counters from counter();
// all will be different copy from each other.
can you make above code scalable ?
// we can make constructor function of the counter to make scalable, so if we want to add new functions like decrement, we can!!
function Counter() {
//good naming convention
var count = 0;
this.increamentCounter = function () {
count++;
console.log(count);
};
this.increamentCounter = function () {
count++;
console.log(count);
};
}
smart garbage collection
function x() {
var i = 1,
j = 10;
return function b() {
console.log(i); //> at this point j is not required so it is handled by garbage collector.
};
}
let y = x();
y();