【 introduction 】

This time I’ll walk through closures from the execution context level.

Execution context

Before we talk about closures, let’s take a look at what we know about the execution context stack and the execution context.

Execution context stack

First of all, there are three types of executable code in JavaScript: global code, function code, and eval code.

When code is executed from top to bottom, preparation is done, which is to create the appropriate execution context. A lot of code corresponds to a lot of execution contexts for easy management. The JavaScript engine creates an Execution Context stack (ECS) to manage many Execution contexts, which you can think of as an array. When JavaScript starts to interpret execution code, it encounters global code first, so initialization creates a global execution context and pushes it into the ECStack, because the ECStack is empty only when the entire application ends, so before the program ends, There is always a global execution context at the bottom of the ECStack. Each time a function is executed, a corresponding execution context is created and pushed into the execution context stack. When the function is finished, the execution context of the function is ejected from the execution context stack.

Execution context

As we mentioned earlier, when JavaScript executes a piece of executable code, it creates the corresponding execution context. My understanding is that the execution context is an environment and scope of the currently executing code. Each execution context contains the following three properties:

  • Variable Object (VO)

    • A variable object is an execution context-specific data scope that stores variable and function declarations defined in the context
  • Scope chain

    • The variable object that contains the previous execution context
  • this

So I’m going to do this with a code here of Hu Yu’s example.

var scope = "global scope";
function checkscope(){
    var scope = "local scope";
    function f(){
        return scope;
    }
    return f();
}
checkscope();
Copy the code

The global code is executed first, creating the global execution context globalContent and pushing it onto the execution context stack

ECStack = [
        globalContext
    ];
Copy the code

The global context is then initialized

    globalContext = {
        VO: [global],
        Scope: [globalContext.VO],
        this: globalContext.VO
    }
Copy the code

When the global context is initialized, the checkScope function is created, and the function saves all parent objects to the internal properties of the function [[scope]]. When the function is executed, the function execution context is pushed and initialized.

Checkscope.[[scope]] = [globalcontext.vo // a variable object in the globalContext that contains all variables and function declarations in the globalContext];Copy the code

When the checkScope function is executed, the corresponding function execution context is created and pushed onto the execution context stack. Initialize the checkScope function execution context after pushing:

  • Copy the function’s [[scope]] scope chain, creating the scope chain
  • Use arguments to create active objects and initialize active objects, i.e. add parameters, function declarations, variable functions (variable objects)
  • Press the variable object at the top of [[scope]]

Now the scope of checkScope looks like this

 Scope: [AO, globalContext.VO],
Copy the code

When the function is executed, the execution context is popped off the stack.

Github.com/mqyqingfeng… Interested can see feather elder brother’s blog, there are detailed instructions inside

closure

Closures defined by MDN are:

Closures are functions that have access to free variables.

A free variable is a variable that can be used in a function but is neither a parameter of the function nor a local variable of the function. So closures consist of functions + free variables that functions can access.

There are two kinds of closures I know of, one theoretical and one practical.

Theoretical perspective:

All the functions. When the global execution context is initialized and the function is created, the function stores all the parent variable objects into the function’s built-in property [[scope]]. The global variables are free variables for the function. So accessing a global variable in a function is equivalent to accessing a free variable.

Practical perspective:

  • It persists even if the context in which it was created has been destroyed (e.g., the inner function returns from the parent)

    • Because a context maintains a chain of scopes, the active variables of its context remain alive in memory even if the context that created it is destroyed
    • Normally, the scope of a function and all variables are destroyed at the end of the function execution. However, after the closure is formed, the scope of the function is preserved until the closure no longer exists. A variable can always be accessed as long as the reference relationship exists.
  • A function that references a free variable in the code

We analyze it with a piece of code

function a(){
    var a = 1
    function b(){
        console.log(a);
    }
​
    return b
}
​
c = a()
c()
Copy the code

This is a classic closure where an inner function returns from a parent function

When c = a() is executed, function B is returned as the return value, so c() is equal to function B, so c() is 1

When a() is executed, the execution context of function A is removed from the execution context stack. However, because function B has a reference relationship to variable A inside function A, it will not be reclaimed by the garbage collection mechanism. C has a reference to B, and B has a reference to the active variable of a function. As long as this reference relationship always exists, B function can always read the active variable of A function through the maintained scope chain

The essence is the lifetime of a variable in a parent scope that is referenced by the parent scope but not released. Causes variables in the parent scope to be released normally after the child scope completes execution.

Scope:[AO, acontext. VO, globalContext.VO] // the Scope of the function b can obtain the value 1 of a from acontext. VOCopy the code

Does the referenced active variable live in the heap or on the stack?

When function A is called, an object called Scope will be created in the heap to ensure that the variable will not be destroyed, and the variable will be stored as the attribute of Scope. So its active variables are not stored in the stack, but in the heap, with a special object Scope. In fact, if it is in the stack, when the execution context of A is ejected, then the active variable will also be ejected. So the active variable of A lives in the heap.

Problems with closures

Heavy use of closures can lead to memory leaks and excessive performance overhead. In JavaScript, garbage collection adopts reference counting method. Every time time polling, variables with a count of 0 in memory will be cleared. Due to the existence of closures, references to active variables always exist, and garbage collection mechanism cannot be cleared. If too many closures are used, the number of variables in memory that cannot be reclaimed increases, causing a memory leak.

Common uses for closures

  1. We want to be able to access variables inside a function outside of it. Private variables can be created by calling the closure function externally, thereby accessing the variables inside the function externally. Ensure the security of variables inside the function, achieve encapsulation, prevent variables from flowing into other environments, naming conflicts, resulting in environmental pollution. That’s why you create private variables instead of free variables.
  2. You want active variables in the context of a function that has run out of time to remain in memory. For example, if it takes time to call a function, we can cache the result in memory, and the result can be returned directly in memory next time, improving the execution efficiency.

I would like to thank Teacher Hu Yui for his article. I learned a lot from it. This article is also written on the shoulders of giants. Interested brothers can go to my blog to play, there are some of my summary of the article, the shortcomings welcome you to criticize.

My blog