This article assumes that you are already somewhat familiar with JavaScript and that you have used JavaScript functions and objects. Chrome Dev Tools (CDT) is intensively used to visualize important steps. You can use another browser if you want, I believe a big part of it can do the same.

In the beginning, there was a Global Scope. And EVERYTHING that you can do was defined there.

Your Global Scope is the Object which contains everything that you can do in its properties. You will see it from browser dev tools (but not yet):

Chrome Dev Tools > Scope > Global Scope

Everytime you creating something in your code, e.g. defining variables, functions, objects, arrays etc. is appearing there, in your Global Object Scope as properties.

Let’s declare an empty function to check it.

function x() {}

Now, in CDT, set your breakpoints like this and run through it (press F8):

Look:

Chrome Dev Tools > Scope > Global Scope

Now lets our “ function x()” will return another “function y()”, we also will define a “variable z” and invoke our “function x()” on it. Later we will check our Global Object Scope.

In the CDT put your breakpoints like this and step over the next function call (press F10 one time):

Hey! Just a second… What’s that? I am 100% sure there was no such Local thing before… 0_o

Chrome Dev Tools > Scope

That’s our Local scope of the “function x()” temporary visible to us. And you can clearly see what this is pointing to (no pun intended).

OK, lets press F10 two more times and check our Global Scope.

Chrome Dev Tools > Scope > Global Scope

Pretty much self-explanatory I guess.

Now let’s do something interesting:

If you are following, just put breakpoints in CDT on line 2 and 7, press F10 three times. Look at it now:

Chrome Dev Tools > Scope > Global Scope

Here is when things get a bit tricky. The Closure is a value in [[Scopes]] array-like object, which have kind of signature Closure (x) (read closure over x), which indicates function named Closure with x as an argument. Even if it looks like the property value — it’s not. There are several ways of how you can be sure about it. If you will copy the property path of Closure(x), instead of [“”0"”].Closure(x), you will get only [“”0"”], on the other hand, if you will do it with val1 you will get [“”0"”].val1. Or you can just try to rewrite it in Object notation (you will not be able to do it). Here is what 0 property actually contains:

0:{type: “closure”, name: “x”, object: {val:1}}

Is this information enough to figure out what kind of x it is? Surely not. We need more context here. And that’s why we have that 1: Global property. It provides us the information about our execution context for Closure (x).

Chrome Dev Tools > Scope > Global Scope

It’s still the old good Global Scope Object just sitting here… as well… Think about it as a linkage, if you want. Here things, as always, are strictly logical. If it’s our Global Scope then it should contain this part as well, everything will repeat… and this chain will never end.

So what’s about closure? Just do one last step:

Because the interpreter has this as a set of scopes:

0: {type: “closure”, name: “x”, object: {val:1}}

1: Global {type: "global", name: "", object: Window}

We can do this z(2), which is equivalent to y(2), and get 3 printed in the console.

Chrome Dev Tools > Scope > Global Scope

And that’s how closures, in general, work in JS.

There are still plenty of technical details under the hood which relates to JS engine, and its surrounding environment. Here you can get a general overview on what the engine does.

P.S.

It seems like that is how closures in general work not only in JS.

Here is a piece of the article from Wikipedia that was greatly optimized in size by sacrificing readability, you know what I mean:

“In programming languages, a closure (also lexical closure or function closure) is a technique for implementing lexically scoped name binding in a language with first-class functions. Operationally, a closure is a record storing a function together with an environment. The environment is a mapping associating each free variable of the function (variables that are used locally but defined in an enclosing scope) with the value or reference to which the name was bound when the closure was created. A closure — unlike a plain function — allows the function to access those captured variables through the closure’s copies of their values or references, even when the function is invoked outside their scope.”