JavaScript — Learn & Understand Closures

Learn the basics of Closures in just 10 minutes

Brandon Morelli
codeburst

--

In this article we’ll explore the basics of closures in JavaScript. Closures are a key aspect of JavaScript that any developer should know about and understand. Lets get started!

Closures Defined.

Lets start with two definitions of closures:

#1: A closure is a function that has access to the parent scope, even after the scope has closed.

#2: A closure is the combination of a function and the lexical environment within which that function was declared.

It’s okay if neither of these definitions make sense to you yet. They will by the end of this article.

Scope

In order to begin to understand closures, we first need to understand what scope is. Below is a brief introduction to Scope. For more information, please see my article: JavaScript: Learn & Understand Scope.

In JavaScript, scope refers to the lifespan and access of a variable.

In other words, where a variable is defined plays a huge role in not only the lifespan of that that variable, but it also determines which functions and code have access to that variable.

Functions in JavaScript have access to variables created both inside and outside of the function. Variables created inside of functions are locally defined variables. A local variable can only be accessed within the function (or scope) that it is defined in.

Ok. Let’s look at an example to hopefully drive this idea home. Below, we have a function called speak(). This function creates a local variable words and then logs out this local variable. Outside of our function we can invoke speak() and 'hi' is logged to the console. However, if we try to log the words variable outside of its scope we get a reference error! words is locally scoped to the speak() function and therefor only accessible within that function.

function speak(){
let words = 'hi';
console.log(words);
}
speak(); // 'hi'console.log(words); // Uncaught ReferenceError: words is not defined

Now, contrast the above code to the below example. In the below code we define words in the global scope instead of the speak() function scope. This means the words variable is now available everywhere in our code. We do not get a reference error and are able to access the variable:

let words = 'hi';function speak(){
console.log(words);
}
speak(); // 'hi'console.log(words); // 'hi'

Nested Functions

Awesome. Now that we understand the basics of scope, what happens when we nest one function inside of another? This is where it gets fun, so I want you to follow along with the next example!

If you’re using Google Chrome, open up your developer console with [WINDOWS]: Ctrl + Shift + J [MAC]: Cmd + Opt + J

Now paste the following code into the console:

function speak() {
return function logIt() {
let words = 'hi';
console.log(words);
}
}

What we’ve done is created a function named speak. speak returns another nested function named logIt. logIt simply creates the variable words and logs the value of that variable to the console.

Awesome. Now we’re going to create a variable and assign it to an invocation of our speak function:

var sayHello = speak();

In case you’ve never seen this before, all we’re doing is invoking the speak function and saving the result of that function invocation to the sayHello variable.

We can actually see what the value of sayHello is by typing the variable into the console and hitting enter:

sayHello;//  function logIt() {
// let words = 'hi';
// console.log(words);
// }

This is really interesting! When we created the sayHello variable the outer speak function executed. So the returned inner function logIt was saved to the sayHello variable.

This means that if we invoke sayHello(), it will invoke and run the logIt()function:

sayHello();
// 'hi'

Awesome! But this isn’t really anything special. We’ve simply created two functions and then executed them in order.

However! By moving just one line of code something very interesting happens! Take a look at the code below. The only thing I’ve changed is I have now moved our words variable declaration outside of the inner function and into the speak() function:

It’s very important you see the difference between the below code and the first speak/logIt functions. If you don’t, scroll up and compare until you see the difference. let words = 'hi' is no longer in the logIt function!

function speak() {
let words = 'hi';
return function logIt() {
console.log(words);
}
}

Now, exactly like before, we’re going to declare a variable and assign it to an invocation of the new speak function:

var sayHello = speak();

And again, we’ll examine our sayHello variable by typing it into the console:

sayHello//  function logIt() {
// console.log(words);
// }

Uh oh. This time sayHello is a little different. There is no words variable. So what’s going to happen when we invoke our sayHello function?

sayHello();
// 'hi'

It still works.

This is what you call a closure.

Lets look back at one of our closure definitions:

A closure is a function that has access to the parent scope, even after the scope has closed.

In the example above, our speak() function’s scope has closed. This means the let words = 'hi' should also be gone. However, because of closures, our inner function ( logIt ) maintains a reference to the scope in which it was created ( speak ). This allows the logIt() function to still access the words variable — even after speak() has closed.

function speak() {
let words = 'hi';
return function logIt() {
console.log(words);
}
}

It’s important to note that every function in JavaScript has this closure effect. There’s nothing you need to explicitly do to a function to get this to work.

Another Example

To drive home this idea we’ll look at one more example. Below we have a pair of nested functions. name() takes one parameter and returns an anonymous function that takes a different parameter. The anonymous inner function returns a string literal:

function name(n) {
return function(a) {
return `${n} likes ${a}`;
}
}

We’ll create two invocations of the name function. For each we’ll pass in a different name:

var j = name('Jacob');
var r = name('Robert');

We can now see exactly what j() is referencing by typing it into the console:

j;//  function (a) {
// return `${n} likes ${a}`;
// }

Awesome. r() appears to be the same:

r;//  function (a) {
// return `${n} likes ${a}`;
// }

But remember, we know from our previous example that because of closures, each function ( j & r ) should still be able to access the n (name) variable from their respective parent scopes. So while they appear to be the same, they aren’t. The n variable in j() references Jacob. While the n variable in r() references Robert.

With this in mind we can pass in the value of a when invoking our new functions:

j('cheese');  // 'Jacob likes cheese'
r('grapes'); // 'Robert likes grapes'

And again, it works! Because of closures we’re able to successfully execute our functions that reference variables from a previously closed scope.

Closing Notes:

Thanks for reading! If you’re ready to finally learn Web Development, check out: The Ultimate Guide to Learning Full Stack Web Development in 6 months.

If you’re working towards becoming a better JavaScript Developer, check out: Ace Your Javascript Interview — Learn Algorithms + Data Structures.

I publish 4 articles on web development each week. Please consider entering your email here if you’d like to be added to my once-weekly email list, or follow me on Twitter.

If this post was helpful, please click the clap 👏 button below a few times to show your support! ⬇⬇

--

--

Creator of @codeburstio — Frequently posting web development tutorials & articles. Follow me on Twitter too: @BrandonMorelli