Comprehensive understanding of closure mechanism

  • 2021-07-02 23:05:56
  • OfStack


var foo = "Hello";
var c =(function a() {
function b(){
var bar = " World";
alert(foo + bar);
return bar;
}
return b;
})()();
alert(foo + c);

This example pops up hello world twice;

1. What is a closure?

The "official" explanation is that the so-called "closure" refers to an expression (usually a function) with many variables and the environment bound with these variables, so these variables are also part of the expression.

I believe few people can understand this sentence directly, because his description is too academic. I want to tell you what a closure is with how to create a closure in Javascript, because it is very difficult to skip the process of creating a closure and understand the definition of a closure directly. Look at the following code:


function a(){
var i=0;
function b(){
alert(++i);
}
return b;
}
var c = a();
c();

This code has two characteristics:

1. The function b is nested inside the function a;
2. Function a returns function b.

Thus, after var c=a (), the variable c actually points to the function b, and after c (), a window will pop up showing the value of i (1 for the first time). This code actually creates a closure. Why? Because the variable c outside the function a references the function b inside the function a, that is:

When function b inside function a is referenced by a variable outside function a, a closure is created.

I guess you still don't understand closures, because you don't know what closures do. Let's continue to explore.

2. What do closures do?

In short, the function of closure is that after a is executed and returned, closure prevents GC, the garbage collection mechanism of Javascript, from reclaiming the resources occupied by a, because the execution of b, the internal function of a, depends on the variables in a. This is a very straightforward description of the function of closures, unprofessional and imprecise, but the general meaning is that understanding closures requires a gradual process.

In the above example, i in a always exists after the function a returns due to the existence of closure, so that every time c () is executed, i is the value of alert after adding 1.

So let's imagine the other case. If a does not return the function b, the situation is completely different. After the execution of a, b is not returned to the outside of a, but only referenced by a, and a will only be referenced by b at this time, so functions a and b refer to each other but are not disturbed by the outside world (referenced by the outside world), and functions a and b will be recycled by GC. (The garbage collection mechanism of Javascript will be described in detail later.)

3. Microcosmic world within closures

For a deeper understanding of closures and the relationship between the function a and the nested function b, we need to introduce several other concepts: the execution environment of the function (ES90context), the active object (call object), the scope (scope), and the scope chain (scope chain). Taking the process from definition to execution of function a as an example, these concepts are expounded.

1. When defining the function a, the js interpreter will set the scope chain of the function a (scope chain) to the "environment" where a is located when a is defined. If a is a global function, there are only window objects in scope chain.

2. When the function a is executed, a will enter the corresponding execution environment (excution context).

3. In the process of creating the execution environment, first, an scope attribute will be added to a, that is, the scope of a, and its value will be scope chain in step 1. That is, a. scope = the scope chain of a.

4. The execution environment then creates an active object (call object). The active object is also an object with properties, but it has no stereotype and cannot be accessed directly through the JavaScript code. After creating the active object, add the active object to the top of the scope chain of a. At this point, the scope chain of a contains two objects: the active object of a and the window object.

5. The next step is to add an arguments property to the active object that holds the parameters passed when the function a is called.

6. Finally, add all the formal parameters of function a and the reference of internal function b to the active object of a. In this step, the definition of function b is completed, so as in step 3, the scope chain of function b is set to the environment defined by b, that is, the scope of a.

At this point, the whole function a is completed from definition to execution. In this case, a returns a reference to b to c, and the scope chain of b contains a reference to the active object of a, which means that b can access all variables and functions defined in a. Function b is referenced by c, and function b depends on function a, so function a is not recycled by GC when returned.

When the function b is executed, it will look like step 1 above. Therefore, the scope chain of b at execution time contains three objects: the active object of b, the active object of a, and the window object, as shown in the following figure:

As shown in the figure, when accessing a variable in the function b, the search order is to search for its own active object first, and return if it exists. If it does not exist, it will continue to search for the active object of the function a, and search in turn until it is found. If it cannot be found in the whole scope chain, undefined is returned. If the prototype prototype object exists in the function b, it searches for its own prototype object after searching for its own active object, and then continues to search. This is the variable lookup mechanism in Javascript.

4. Application Scenarios for Closures

1. Protect the variables in the function. Taking the initial example as an example, i in function a can only be accessed by function b, but cannot be accessed by other ways, thus protecting the security of i.

2. Maintain 1 variable in memory. As before, the 1 of i in the function a exists directly in memory because of the closure, so every time c () is executed, i is self-added by 1.

The above two points are the most basic application scenarios of closures, from which many classic cases originate.

5. Garbage collection mechanism of Javascript

In Javascript, if an object is no longer referenced, the object is reclaimed by GC. If two objects refer to each other and are no longer referenced by the third object, the two mutually referenced objects will also be recycled. Because the function a is referenced by b, and b is referenced by c outside a, this is why the function a is not recycled after execution.


Related articles: