Closures
Hey ,
I'm thrilled to help you learn JavaScript. Unfortunately, you've landed on a page where you cannot access with your current purchase.
Please upgrade (use this link) access this content.
I'm super eager to help you learn more!
Hey ,
I'm thrilled to help you learn JavaScript. Unfortunately, you've landed on a page where you cannot access with your current purchase.
Please upgrade (use this link) access this content.
I'm super eager to help you learn more!
A closure is created when you have two functions—one of these functions is inside the second function.
// Closure pattern
function outside () {
function inside () {/* ... */}
}
Closures prevent variables declared within outside
from leaking out into the global scope.
function outside () {
const message = 'Hello World!'
}
// Run the function
outside()
// Tries to log message
console.log(message)
But they let inside
use the variables declared within outside
.
function outside () {
const message = 'Hello World!'
function inside () {
console.log(message)
}
// Runs inside
inside()
}
outside() // Runs outside
Closures become powerful when you return inside
. When you do this, you can run inside
later.
function outside () {
const message = 'Hello World!'
return function inside () {
console.log(message)
}
}
Closures remember values within outside
's lexical scope. When you run inside
, you still have access to the variables declared within outside
.
const inside = outside()
inside() // Hello World!
You can pass values into outside
to store them. You can then use them later when you need to.
function outside (message) {
return function () {
console.log(message)
}
}
const inside = outside('Hello World!')
inside() // Hello World!
If you wish to pass arguments into inside
, you can return a function that takes in arguments.
function outside (message) {
return function (additionalMessage) {
console.log(message + ' ' + additionalMessage)
}
}
const inside = outside('Hello')
inside('World!') // Hello World!
You can use arrow functions to create closures. No worries there! Arrow functions make things easier to read since there’s no function
keyword overhead.
function outside (message) {
return () => {
console.log(message)
}
}
const inside = outside('Hello World!')
inside() // Hello World!
Remember to follow the Arrow Functions Best Practice I explained in This in JavaScript!
A closure is actually created when you nest a scope within another scope (not just functions in functions). We don’t usually recognise them as closures, but they are closures.
So if you return an object as inside
, you return a closure too. The object contains information about outside
's lexical scope.
function outside (message) {
return {
message
}
}
const inside = outside('Hello World!')
console.log(inside.message) // Hello World!
This pattern is often used in Object Oriented Programming, where you return properties and methods you want to expose. (Hello Factory Function!)
function Human (firstName, lastName) {
return {
firstName,
lastName,
sayFirstName () {
console.log(firstName)
}
}
}
const zell = Human('Zell', 'Liew')
zell.sayFirstName() // Zell
console.log(zell.lastName) // Liew
When you use closures, you don’t need to use this
at all! The instance remembers all declared variables because they’re inside a closure.
Let’s say we have a list of elements. When we click on each element, we want to console.log
the element’s text content.
One way to do this is to create a textContent
variable. We will store the text content inside this textContent
variable. Then, we log textContent
inside an event listener.
const list = document.querySelectorAll('li')
for (const li of list) {
const textContent = li.textContent
li.addEventListener('click', () => {
console.log(textContent)
})
}
In this case, each callback remembers its textContent
value. When you click the li
, they will log this value—even if the actual text was changed later.
We learned this while going through the bind
method. Here, we can use bind
to predetermine variables we want to pass into a function.
function sayName (firstName, lastName) {
console.log(`${firstName} ${lastName}`)
}
const sayZellName = sayName.bind(null, 'Zell')
sayZellName('Liew') // Zell Liew
We’ll explore this use case in the next lesson.