Closures are easy, you're just freaked out by the number of articles on the web. It's a really simple idea. Pay attention to my explanation.

First of all, I would like to summarize, in fact, closures are just a byproduct of the chain of scope, to understand the closure completely, must first take the chain of scope!

Next comes the scope chain.

A chain of scopes

But before we can tackle scope chains, we need to know the lexical context.

In JS, every running function, block of code, or entire program, has an associated object called the lexical environment.

The lexical environment consists of two parts:

So, variables are just attributes that the environment records for this particular internal object. Accessing or modifying a variable means accessing or changing a property of the lexical environment.

Take an example of a global lexical environment: For the example above, this is a global lexical environment associated with the entire script, with rectangles representing environmental records (where variables are stored) and arrows representing references to the external lexical environment. Since the global lexical environment has no external references, we point to NULL here.

Let's move on to a more detailed explanation of the figure above:


  • When the script starts running, the lexical environment is prepopulated with all declared variables. Initially, they are in the “Uninitialized” state. This is a special internal state, which means that the engine knows about the variable, but is not allowed to use it before the LET. It’s almost as if the variable doesn’t exist.
  • Then until the “let name” statement comes up, name is undefined because it is not initialized, but we can use this variable.
  • Next to the execution of “name = Xiaoming”, the name variable is assigned a value.

That explains the lexical context of variables, then we look at function declarations.

For functions, a function is also a value, just like a variable. Unlike a variable, function declarations are completed immediately upon initialization.

When creating a new lexical environment, functions declared by functions are immediately available. (Unlike variables that can't be used until they are declared)


Normally, this applies only to function declarations, not function expressions.

Now let's look at the more complex lexical context.

When the SAY function is called, a new lexical environment is immediately created to store variables and arguments in the function.


When the SAY function is executed, there are two lexical environments, one is the lexical environment of the SAY function, the other is the lexical environment of the global (JS file).

  • The say function has an attribute in its lexical environment that when we call say (” Go China! ), so the value of words is “Go China! .
  • The external lexical environment reference to the SAY function points to the global lexical environment, which has say and name.

When code accesses a variable — it searches the internal lexical context, then the external context, then the more external context, and so on, all the way to the global lexical context. This is the scope chain. Go up level by level until you find it. If you don’t find it, report an error.

Let's demonstrate the search process:

  • For the words variable, the value of words is immediately found in the lexical environment.
  • For the name variable, it will be searched in the say function lexical environment first. If it cannot find the name variable, it will be searched in the global lexical environment.

Scope chain: When a variable is searched, it is searched from the current environment record object. If it is not found, it is searched from the parent (lexical parent) environment record object until it finds the global environment record object, which is the global object. Thus a linked list of multiple environment record objects is called a scope chain.

After understanding the scope chain, it's time to move on to the highlight of the day, closures!

The second closure

I'm going to use a counter example to illustrate closures!

function makeCounter () {
    let counter = 0;
    return function () {
        return counter++;
    }
} let counter = makeCounter(); Copy the code

When the makeCounter() function is called, a new lexical environment is created to store variables from the mackCounter runtime. The diagram below:


When makeCounter() was executed, we created a nested function: return counter++, but it was not executed, just created.

Each function is created with its lexical Environment in mind, and references to these lexical environments are permanently stored in the function's hidden attribute [[Environment]]. As shown below:


Therefore, counter.[[Environment]] has a lexical Environment reference to {counter: 0}. This is why the function remembers where it was created, regardless of where the function was called.

When counter() is called, a new lexical Environment is created, and the values referenced by the external lexical Environment are taken from the values in the function [Environment]]. The diagram below:

When counter() looks for the counter variable in its lexical context and doesn't find it (the context record object is empty), it looks in its parent (the makeCounter lexical context), finds the counter variable, and changes the value of the variable in the context record object.

Update variables in their lexical environment.


That's where closures come in, so closures are just byproducts of scoped chains.

A closure is an inner function that can always access variables and arguments declared in its outer function, even after its outer function has been returned (end-of-life). In some programming languages, this is not possible, or functions should be written in a special way to do this. But as mentioned above, in JavaScript, all functions are inherently closed (with one exception, which I'll cover in the "new Function" syntax). That is: functions in JavaScript automatically remember where they were created via the hidden [[Environment]] property, so they all have access to external variables.