Closures In JavaScript
Mavis
Posted on February 8, 2023
Definition from 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.
Playground
const sayHi = () => {
const name = 'Nic'
function sayIt(){console.log(`Hi, ${name}`)}
return sayIt
}
const sayWhat = sayHi()
sayWhat()
In above case, what happen when call sayWhat()
, the result is Hi, Nic
.
If a function return something which have some references to this function scope variables, then the variables will be moved to closure box
instead of collecting by garbage.
As sayHi
contain sayIt
, and sayIt
is executed when sayWhat
was called. Then the variable name
in the sayHi
will be removed to closure box as it's used in sayIt
. when sayIt
is executed, it will look for the name
in its scope first, if cannot find it, then go to outer closure box to search. That's how closure works.
const sayHi = () => {
setTimeout(() => {console.log(name)}, 4000)
const name = 'Oodie'
}
sayHi()
In above case, what happen when call sayHi()
, the result is Oodie
. The explanation is the same as above.
What's the benefit from closures
Memory Efficient
In below case, every time we call heavyDuty function, we need to create a memory to store bigArray, as we call it many times, so it's kind of waste our memory. You may say, we can create big array outside of heavyDuty
function. But that will pollute the global scope. That's why we apply closure here.
function heavyDuty(index){
const bigArray = new Array(10000).fill('I am a big array')
console.log('created!')
return bigArray[index]
}
heavyDuty(8888) // created
heavyDuty(8888) // created
heavyDuty(8888) // created
Let apply closure to above case. We only need to create big array one time. And we can use it anywhere and anytime.
function heavyDutyWithClosure(){
const bigArray = new Array(10000).fill('silly')
console.log('created!')
return function(index){
// bigArray will always refer to outside bigArray
return bigArray[index]
}
}
const getHeavyDuty = heavyDutyWithClosure()
getHeavyDuty(8888)() // created
getHeavyDuty(8888)()
getHeavyDuty(8888)()
Encapsulation
const makeNuclearButton = () => {
let timeWithoutDesctruction = 0;
const passTime = () => timeWithoutDesctruction++;
const totalPeaceTime = () => timeWithoutDesctruction++;
const launch = () => {
timeWithoutDesctruction = -1;
return "boom";
};
setInterval(passTime, 1000);
return {
launch: launch,
totalPeaceTime: totalPeaceTime,
};
};
const ohno = makeNuclearButton();
console.log(ohno.totalPeaceTime()); // 0
// after 10 second, execute this
console.log(ohno.totalPeaceTime()); // 10
// after 2 second, execute this
console.log(ohno.totalPeaceTime()); // 12, as setInterval is working
In above case, you can not change timeWithoutDesctruction, but can get it.
what if we don't want you to get launch function? just remove it from return object.
Exercise
Try to convert them by using closures.
Q1
let view
function initialize(){
view = 'boom'
console.log('view has been set!')
}
initialize() // view has been set!
initialize() // view has been set!
initialize() // view has been set!
Result
let view
function initialize(){
let called = 0
return function(){
if(called > 0){
return;
}else{
view = 'boom'
console.log('view has been set!')
called++
}
}
}
const startOnce = initialize()
startOnce() // view has been set!
startOnce()
startOnce()
Q2
const array = [1,2,3,4]
for(var i=0; i<array.length; i++){
setTimeout(function(){console.log('I am at index ' + i)}, 3000)
}
// I am at index 4
// I am at index 4
// I am at index 4
// I am at index 4, as var do not have block scope
Result
// easy way to fix them by keyword let
const array = [1,2,3,4]
for(let i=0; i<array.length; i++){
setTimeout(function(){console.log('I am at index ' + i)}, 3000)
}
// I am at index 0
// I am at index 1
// I am at index 2
// I am at index 3
// easy way to fix them by keyword let
const array = [1,2,3,4]
for(var i=0; i<array.length; i++){
setTimeout(function(){console.log('I am at index ' + i)}, 3000)
}
// I am at index 0
// I am at index 1
// I am at index 2
// I am at index 3
// let fix it by using IIFE
const array = [1,2,3,4]
for(var i=0; i<array.length; i++){
(function(closureI){setTimeout(function() {console.log(closureI)})})(i)
}
// I am at index 0
// I am at index 1
// I am at index 2
// I am at index 3
Learn more about the IIFE
Posted on February 8, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 29, 2024