britrunner

aaron cooper

19 Mar 2019

Explainable JavaScript: Scope & Closures

Scope in JavaScript is easy to grasp and use, but often difficult to explain well to someone else. The same generally applies to closures as well, which has the added difficulty when it comes to differentiating it from scope.

The goal of this article is to break down what the different types of scope are in JavaScript, have a look at what makes closures a different kettle of fish, and finally: how we can eloquently explain these two concepts to someone else in a few short sentences.

Scope

Scope is a mechanism built into JavaScript that defines where declared variables and functions can be accessed from. You might declare var fruit = 'apple'; in one place, but where in your code can this variable be “seen” from?

Scope in JavaScript comes in a few different forms, so let’s go through each of them.

Global scope

Global scope is quite literal: anything that’s in the global scope is available anywhere in your code, after it is defined. You may have heard that it’s bad practice to use or put things into global scope, and with good reason— if it’s global and accessable from anywhere, how can you be sure when you access a global variable that it a) exists, and b) has not been overwritten by a piece of code somewhere else?

Here’s a variable being declared globally:

fruit = 'apple';

Notice that it has no var keyword in front of it. Omitting var tells the JavaScript parser to make it globally available.

Note: If you use the "use strict" directive at the top of a JavaScript file or function, defining global variables like above will throw an error.

Here’s another way of declaring a global variable that can also be used when in strict mode:

window.fruit = 'apple';

Or in NodeJS (which doesn’t have a native window object):

global.fruit = 'apple';

Local scope

If global scope makes a variable accessible from anywhere, it’s a fair guess to say that local scope restricts where a variable can be accessed from, which it does. If a variable is declaring with the var keyword, it is treated as a locally scoped variable, which means that it can only be accessed from within the context that it’s defined in.

A new context is created for every function body, with means that variables declared inside a function are only accessible within the function itself.

function basket() {
var fruit = 'apple';
console.log(fruit);
}
basket();
console.log(fruit);

In the above example, console.log gets called twice, once from inside the function, once outside the function. The result in the browser console will look like this:

'apple'
undefined

Notice that the first two calls return the expected value. The third doesn’t, because it can’t. The third console.log call is made outside of the function that contains the local variable, so it cannot access it.

Sometimes local scope is referred to as “Function scope” in JavaScript as well, because functions create a new local scope in their body.

On a related note, for, while, and if all have blocks of code, but defining variables in their blocks will place the variable into the parent scope. However, the new const and let keywords available in the latest versions of JavaScript do allow variables to be defined in any of these blocks and they will not be accessible outside.

Lexical scope

Lexical scope is special: it’s a way for a nested function to have access to anything declared locally in the parent scope(s).

Take a look at the following code, can you guess what the second console.log call, made from within the getFruit function will produce?

function basket() {
var fruit = 'apple';
console.log(fruit); // 1
var getFruit = function() {
console.log(fruit); // 2
};
getFruit();
}
basket();
console.log(fruit); // 3

Running the code results in these values:

'apple'
'apple'
undefined

So this goes to show that functions within functions have access to what’s “above” them, in the parent scope. It’s not just the immediate parent either, it’s all parents above that are accessible, as in the below example:

function basket() {
var fruit = 'apple';
var getFruit = function() {
var reallyGetFruit = function () {
var seriouslyGetFruit = function () {
console.log(fruit);
};
};
};
}

Scope: A definition

As we just covered, the three main types of scope in JavaScript are: Global, Local and Lexical. But how can we wrap up all this knowledge into a nicely presented description that you can fire off at will? Well, here’s my shot at it:

Scope in JavaScript refers to the context that a variable is defined and accessed in. In JavaScript, scope can be global, local or lexical. Globally scoped variables are accessible anywhere after they are defined. Locally scoped variables are not accessible outside the context of a function body. Lexical scope refers to all of the parent scopes of a function, all the way up to the global scope.

Closures

I remember being asked what a Closure is in JavaScript back in my early interviewing days and I’m pretty sure I screwed it up a lot. It’s one of those questions I hated being asked because even though I understood that closures are created by functions, I didn’t have a well-defined description of what closures really are that I could whip out at short notice. If you feel the same way, let’s fix that right now.

What a Closure is

A closure is a piece of code encapsulated in a function body, but there’s a bit more to it than this. A closure is a bit of code that you can think of as having its own little environment, a scope, that occupies a piece of memory. This memory can hold variables and their current values or defined functions.

Here’s an example showing how whatever is defined in the body of a function stays around even after a function is first invoked:

function basket() {
var fruit = 'apple';
var getFruit = function() {
return fruit;
};
return getFruit;
}
var getFruitInside = basket();
var myFruit = getFruitInside();

On line 11, basket() get called, which returns the function declared inside: getFruit. Then on line 12 that returned function, which is now stored inside the variable getFruitInside, is called and the result is stored in myFruit. Pay attention to the fact that getFruit on line 5 attempts to return fruit, which is in the lexical scope.

Now have a think. What should the value of myFruit be?

Perhaps I wrote the code in such a way that would lead you into thinking that the resultant value of myFruit would be the value of fruit, which is 'apple'. Well, it actually is this value.

If you think about it, it’s a tad weird. How can the locally scoped fruit variable value make its way out through the return value of the inner getFruit? Lexical scope is one thing— in the lexical scope examples we simply used console.log to read the variable directly. But what’s now happenning in this example is that the lexically available fruit variable is being persisted in such a way that makes it readable even when we’re not calling the parent function directly.

This is the essence of what a closure is.

Closures: A definition

Now for the hard part, wrapping up our understanding in a short paragraph. We need to make sure the definition differentiates itself clearly enough from scope, which is a very closely related topic and easy to get mixed up with. So without further babble, here’s my attempt at summing up what a closure is:

A closure is a piece of code inside a function body whose local variables get stored in memory when the parent function is invoked. Because the variables are stored in memory, this allows functions to be defined inside the parent function that can be returned and invoked later. These returned functions will have access to the lexical variables defined in the parent function even after the parent as already finished execution.

Summary

Scope and Closures are two pivotal concepts in JavaScript that are important to know, regardless of skill level. As with anything really, the more you learn, the less important the fundamentals seem, but it’s always good to go back and look at them from time to time. I enjoyed having a bash at writing up shortish definitions of these two. I felt it was about time I revisited them myself and I hope these were helpful to someone out there!