A quick dip into Closures.

Closures have been a topic I’ve been slow to understand, but that’s ok! This happens over the span of a web developers journey. I’ve been finding some great resources around Medium and other sites that have made the concepts clearer to me. Here’s my first attempt at explaining Closures, I hope it helps!

This post will cover:

  1. What’s a Closure?
  2. Why are they important?
  3. Closures in action

What’s a Closure

Every time a function is created so is closure. This closure creates an environment where explicit rules are introduced regarding a functions ability to access the global and functional scope.

A mouthful I know, but just think of it like this: when we create our function we need to know what variables we have access to and our closures will dictate this. Whether there’s a function within a function or a function sitting in our global scope, closure dictates how variables will be passed down.

Notice I said passed down? That’s important to note because hierarchically speaking functions can only pass inheritance down and not back up. This is a little complicated but once we see it in action it’ll be a breeze.

Why Closures Matter

Closures are important because knowing how our functions inherit variables will allow us to build them in a way that keeps them running properly.

Being aware of the relationships between functions and their scope will allow us to write code quickly and efficiently.

Closure in Action

Let’s start by looking at a basic closure so we can understand the relationship at play.

let name = "Matt";
function nameChange() {
return name
}
console.log(nameChange()); // Output: Matt

In this example, our function is looking for the name variable. Initially, it will scan its own scope, but if there is no variable defined there the function will escape to the global scope and locate the appropriate variable there. In a sense, it’s borrowing the name variable from the outside scope.

Let’s dive a bit deeper, what happens if there’s a variable defined inside and outside our variable?

let name = "Matt";
function nameChange() {
let name = "Tony"
return name
}
console.log(nameChange()); // Output: Tony

When the function is invoked, it scans its functional scope first before expanding out. So it identifies the name variable set to Tony and proceeds to return that value.

The global let name = "Matt"; is never touched in this instance. Meaning, if we executed console.log(name) after invoking the function nameChange() it would return Matt not Tony.

However, if instead of declaring a new name variable inside the nameChange() function we reassigned the name variable with the syntax name = "Tony". Then when the function is invoked, it would recognize the reassignment, and then search its local scope for the proper name variable to reassign to Tony.

Once that failed, it would then search the global scope, and find let name = "Matt", then it would reassign the value to Tony and return that. In this instance if we console.log(name) after invoking the function nameChange() it would return Tony not Matt.

Big steps here! Before we close, I want to review one last thing and that’s closure when there’s a function within a function. It is similar to what we just saw, but between two functional scopes instead.

let firstName = "Matt"; // 1
function fullName() {
let lastName = "Basile" // 2
function logName(){
let name = `${firstName} ${lastName}`; // 3
return name
}
  console.log(logName());  // 4
};
fullName(); // 5

I’ve labeled the critical components of this closure example and will go through one by one now:

  1. Defining a global variable
  2. Defining a local variable to fullName(). The global scope cannot access it, but their functional scope can.
  3. Defining a local variable to logName().
  4. A console.log that invokes the logName() function. This is within the fullName() scope.
  5. Our fullName() function is invoked, which also leads to the running of logName(). During this time logName() searches their scope for the variable definitions. Once they cannot locate they cascade upwards to the fullName()and global scope to find the proper variable. Once completed we should see the result Matt Basile in our console.

As mentioned before we can see a lot of the principles from our prior examples being portrayed here. Just remember that although logName() can access the variables in fullName()and global scope the inverse cannot happen. However, fullName()still has access to theglobal scope but the global scope will never have access to any functional scopes within it.

Wrap Up

Another whacky concept, but closures will help us understand how we can inherit variables throughout our code. Keeping these relationships in mind will go a long way to preserving our structure and logic.

Lastly, here’s an article by Samer Buna I found useful in contextualizing closure. I love the car analogy and also found Samer’s guided demo to be wonderful as well.

As always, please reach out if you have any questions, Happy Coding!