There are a lot of different ideas about closures, and today we’re going to go through them.

What is a closure

A combination of a function bundled with references to its surrounding state (lexical environment) is called a closure. That is, closures allow you to access the scope of an outer function within an inner function. In JavaScript, whenever a function is created, the closure is created at the same time the function is created. – the MDN

So what is the lexical context? What does the lexical environment involve? Why lexical context for definition?

We talk about closures, scope chains, execution contexts, and this values, but they’re all about the same thing: the execution of a function. Let’s take a look at these concepts and see if we can find any answers to them.

Execution context

The JavaScript standard has changed over the years, and the content of the execution context has also changed dramatically. Many students still use the DEFINITION of ES3, which varies from community to community. Ecma-262, 11th Edition, June 2020

Ten years passed between the release of ES3 in 1999 and the release of ES5 in 2009. The release of ES4 in the middle was abandoned because of the great changes in features and the disagreement within ECMA. Many people opposed it. So ES3 is also a version we know.

Let’s take a look at what each version of the execution context has.

ES3

The ES3 execution context consists of three parts:

  • A scope chain is also called a scope chain.
  • Variable object: a variable object used to store variables.
  • This value: This value.

ES5

Improved naming to change the first three parts of the execution context to look like this.

  • Lexical environment: lexical environment, used when fetching variables.
  • Variable environment: used when declaring variables.
  • This value: This value.

ES2018

By this version, the changes were substantial, and the this value was subsumed by the lexical Environment, with a few additions.

  • Lexical environment: lexical environment, used when fetching variables or this values.
  • Variable environment: used when declaring variables.
  • Code Evaluation State: Used to restore the code execution location.
  • Function: used when the task being executed is a Function.
  • ScriptOrModule: used when executing a ScriptOrModule, indicating the code being executed.
  • Realm: Base libraries and built-in object instances to use.
  • Generator: Only the Generator context has this property, indicating the current Generator.

Are there any differences between the terms specified in ECMA 262 and those you know? Welcome to leave a message. The next step is to see how the function is executed.

Function execution procedure

Let’s look at the following JavaScript code:

this.a = 2;
var b = {}
let c = 1
Copy the code

To implement it correctly, we need to know the following:

  • Where var declares b;
  • B is what variable;
  • Which object is the prototype of B;
  • Where let declares c; Which object does this refer to?

This information needs to be given by the execution context. The code appears in different places, and even in different execution contexts, so the same code can produce different behavior.

The back-and-forth calls to function execution are more complex, so let’s look at the following code, which has three js files:

In the main. Js can access the variable I, at the same time, the function called foo, foo can access the variable x, function foo and call the function inside foo2, foo2 can access the variable y.

Foo2 is called in foo, but x is not accessible, which is why closures are functions and the lexical context in which the function is defined, y is the lexical context in which foo2 is defined, and x is the lexical context in which foo2 is executed.

  • The code in blue accesses the variable I
  • The code in yellow can access the variable X
  • The code in purple can access the variable y
  • This progression from accessible to inaccessible to accessible is the nature of the stack. Function calls themselves reside on the stack, and accessible variables can also be described on the stack.

Execution context stack

As we know from the above example, the variables to be accessed by each line of code execution are stored in one place, which is the execution context. This place is a collection of multiple execution contexts. We call this structure the execution context stack, as shown in the following figure.

The top element of this stack is the Execution Context that we can access when executing a line of code, and it has a special name called Runing Execution Context. All information required for code Execution can be retrieved from the Runing Execution Context. What could be in it? Variables, this, where the function is executed, the prototype needed to declare an empty object…… It’s all in here. The main scenario of switching execution context is function call. The execution context will be switched when the function is called and the function is executed.

Execution Context (ES2018)

Each time control is transferred from executable code associated with the execution context currently running to executable code that is not associated with that execution context, a new execution context is created. The newly created execution context is pushed onto the stack as the running execution context. In the example above, when Foo calls foo2, a new Execution Context is created and becomes a Runing Execution Context.

Different execution contexts have different components. All execution contexts have the following components:

  • code evaluation state
  • Function
  • Realm
  • ScriptOrModule

ECMAScript Code Execution Context

  • LexicalEnvironment
  • VariableEnvironment

Generator Execution Contexts additional include:

  • Generator

The execution context is purely a canonical mechanism and does not need to correspond to any specific artifact of the ECMAScript implementation. It is not possible for ECMAScript code to directly access or observe the execution context.

Now that we know how the execution context works, and what is involved in the execution context, and have a general idea of where the C declaration at let c = 1 goes, let’s take a closer look at the instantiating Environments important device.

Lexical Environments

We mentioned earlier that this and variable declarations are saved in Lexical Environments, as well as super(), new.target, and so on.

A lexical Environment consists of an Environment Record and a possible empty reference to an external lexical Environment. Typically, the lexical context is associated with some particular syntactic structure of ECMAScript code, such as the FunctionDeclaration, BlockStatement, or TryStatement Catch clause, A new lexical environment is created each time the code is evaluated.

Environment Records
  • Declarative Environment Records

    Each Declarative Environment Record is associated with an ECMAScript program scope that contains variables, constants, lets, classes, modules, imports, or function declarations. Declarative Environment Record binds a set of identifiers defined by declarations within its scope, such as C :1, where let C = 1 is declared.

  • Object Environment Records

  • Function Environment Records

  • Global Environment Records

  • Module Environment Records

Declarative Environment Records Declarative Environment Records Declarative Environment Records Declarative Environment Records Declarative Environment Records Declarative Environment Records Declarative Environment Records Declarative Environment Records The Function generates Function Environment Records.

Rediscover closures

According to the golden definition of a closure, a closure consists of two parts:

  • Part of the environment
    • The environment
    • Identifier list
  • Expression part

The term closure does not appear in the JavaScript standard, but it is not difficult to find the JavaScript equivalent of a closure component based on the classical definition.

  • Part of the environment
    • Context: the lexical context of a function (part of the execution context)
    • Identifier list: Undeclared variables used in a function
  • Expression part: function body

In JavaScript, each function contains a lexical context, because ECMAScript Code Execution Contexts is introduced above and LexicalEnvironment is included. The function body is the expression part of the closure. An identifier list is an undeclared variable used in a function.

Here’s an example:

Function foo2 is defined with a var y = 2, so wherever foo2 is passed as an argument, export, or import, it will carry the variable y = 2, which is stored in the Environment Record. This is a closure. This is also the key facility for our Environment Records to form a chain structure.

Let’s look at a more complicated example:

Function foo3 is an arrow function returned by foo2. The arrow function uses the z = 3 variable defined by foo2, so z:3 is stored in the Environment Record. And foo2’s Environment Record Y :2 is saved as the superior of Z :3, which is the chain structure formed by Environment Records. In earlier versions it was called Scope Chain (ES3, 10.1.4 Scope Chain and Identifier Resolution), but in ES2018 it has changed.

Since foo3 is the arrow function, this is also saved, plus z:3 and the outer y:2, so the arrow function can access this, z, and y.

This is how closures and scope chains work. A common misconception here is that some people think of the JavaScript execution context, or Scope (part of the execution context specified in ES3), as a closure. In fact, the JavaScript equivalent of a closure is a “function.”

It is important to note that closures are seen differently depending on the host environment, as in this example:

var i = 1
var foo = function () {
  console.log(i)
  debugger
}

foo()
Copy the code

Closures are generated in Chrome, but not in Node, so be careful not to rely on the behavior of the host environment when experimenting.

Since every function might generate a closure, how many JavaScript functions are there, and how do we determine the value of this for different functions? We’ll talk about that next time.

Finally, how many closures does this code generate?

var i = 1

var foo = function () {
  var x = 2
  var y = 3; (function () {
    console.log(x, i)
  })()

  var foo2 = function() {
    var z = 4
    console.log(y)

    return () = > {
      console.log(x, y, z, i)
    }
  }
  
  return foo2()
}

foo()()
Copy the code

If there are mistakes welcome to point out, welcome to discuss together!