Detailed Explanation of js Closure and Garbage Collection Mechanism Example

  • 2021-11-01 01:51:06
  • OfStack

Preface to the table of contents
Text
1. Closures
1.1 What are closures?
1.2 Properties of Closures 1.3 Understanding Closures
1.4 Main implementations of closures
1.5 Advantages and disadvantages of closures
1.6 Use of closures
2. Summary of garbage collection mechanism

Preface

Closure and garbage collection mechanisms are often difficult points in front-end learning development, and they are often encountered in interviews. This paper records 1 notes on this aspect in learning work.

Text

1. Closures

Closure (closure) is a difficult and characteristic of Javascript language, and many advanced applications depend on closure. As an JavaScript developer, it is important to understand closure 10 points.

1.1 What are closures?

Closures are created when one function refers to a variable of another function, and the inner function is returned to the outside and saved. (The scope chain AO of the inner function uses AO of the outer function.)

Because the variable is referenced, it will not be recycled, so it can be used to encapsulate a private variable, but unnecessary closures will only increase memory consumption.
Closure is a mechanism to protect private variables, which forms a private scope when a function is executed, and protects private variables from external interference. Or a closure means that a child function can use the local variables of the parent function and the parameters of the parent function.

1.2 Properties of closures

① Function nesting function

② Parameters and variables outside the function can be referenced inside the function

Parameters and variables will not be collected by garbage collection mechanism

1.3 Understanding Closures

Based on our familiar knowledge of scope chain, let's look at the counter problem, how to implement a function, and the counter is incremented by 1 every time the function is called.


var counter=0;
 function demo3(){
 console.log(counter+=1); 
 }
 demo3();//1
 demo3();//2
 var counter=5;
 demo3(); //6

In the above method, if the value counter of counter is changed in any place, it will fail. javascript uses closures to solve this problem, that is, functions embedded inside functions. Let's look at how to realize them by using closures.


function add() {
 var counter = 0;
 return function plus() {
 counter += 1;
 return counter
 } 
 }
 var count=add()
 console.log(count())//1
 var counter=100
 console.log(count())//2

The above is an example of closure use, An plus function is embedded in the function add, count variable references the return function, every time the external function add executes, it will open up a block of memory space. If the address of the external function is different, it will re-create a new address, and the plus function is nested inside the add function, thus generating the local variable counter, calling the count function, and adding 1 to the local variable value, thus realizing the real counter problem.

1.4 Main implementations of closures

Here, we mainly learn closures in two forms:

Function as the return value, which is used in the above example.


function showName(){
 var name="xiaoming"
 return function(){
  return name
 }
 }
 var name1=showName()
 console.log(name1())

Closures are functions that can read variables inside other functions. Closure is a bridge connecting the inside and outside of a function.

② Closures are passed as parameters


var num = 15
            var foo = function(x){
                if(x>num){
                    console.log(x)
                }  
            }
            function foo2(fnc){
                var num=30
                fnc(25)
            }
            foo2(foo)//25

In the above code, the function foo is passed into the function foo2 as a parameter, and when foo2 is executed, 25 is passed into foo as a parameter, and then the x is judged > The num value of num is num in the scope of the creation function, that is, num globally, instead of num inside foo2, so 25 is printed.

1.5 Advantages and disadvantages of closures

Advantages:

Protect the variables in the function, realize encapsulation, and prevent the variables from flowing into other environments to cause naming conflicts

(2) Maintain a variable in memory, which can be used as cache (but it is also a disadvantage and consumes memory)

Anonymous self-executing functions can reduce memory consumption

Disadvantages:

(1) One of the above points has been reflected, that is, the referenced private variable cannot be destroyed, which increases memory consumption and causes memory leakage. The solution is to manually assign it to null after using the variable;

Secondly, because closures involve cross-domain access, it will lead to performance loss. We can reduce the impact on execution speed by storing cross-scope variables in local variables and then accessing local variables directly.

1.6 Use of closures

for (var i = 0; i < 5; i++) {
      setTimeout(function() {
      console.log( i);
     }, 1000);
    }

    console.log(i);

Let's look at the above question. This is a very common question, but what will this question output? Everyone knows that the output result is 5, 5, 5, 5, 5, 5. If you look carefully, you may find that this question still has many ingenious points. What is the output order of these 6 5? 5- > 5, 5, 5, 5, 5. It is not difficult for people who know synchronous and asynchronous to understand this situation. Based on the above problems, let's think about how to implement 5- > What about the sequential output of 0, 1, 2, 3 and 4?


for (var i = 0; i < 5; i++) {
 (function(j) { // j = i
  setTimeout(function() {
  console.log( j);
  }, 1000);
 })(i);
 }
 console.log( i);
//5 -> 0,1,2,3,4

In this way, anonymous functions are added to the for loop, and the parameters of anonymous functions are the values of i every time. After 1 second of synchronization function output 5, 01234 will continue to be output.


for (var i = 0; i < 5; i++) {
 setTimeout(function(j) {
  console.log(j);
 }, 1000, i);
 }
 console.log( i);
 //5 -> 0,1,2,3,4

If you look closely at setTimeout's api, you will find that it has a third argument, which eliminates the problem of passing i through anonymous functions.


var output = function (i) {
      setTimeout(function() {
      console.log(i);
     }, 1000);
    };

    for (var i = 0; i < 5; i++) {
      output(i); //  It's passed here  i  Value is copied 
    }

    console.log(i);
    //5 -> 0,1,2,3,4

The same effect is achieved here by using closures to pass function expressions as arguments into the for loop.


for (let i = 0; i < 5; i++) {
        setTimeout(function() {
            console.log(new Date, i);
        }, 1000);
    }
    console.log(new Date, i);
    //5 -> 0,1,2,3,4

Those who know the let block-level scope will think of the above method. But if you want to implement 0- > 1 - > 2 - > 3 - > 4 - > What about this effect?


for (var i = 0; i < 5; i++) {
      (function(j) {
      setTimeout(function() {
       console.log(new Date, j);
     }, 1000 * j); //  Modify here  0~4  Timer time of 
     })(i);
    }

    setTimeout(function() { //  Add a timer here, and the timeout is set to  5  Seconds 
      console.log(new Date, i);
    }, 1000 * i);
    //0 -> 1 -> 2 -> 3 -> 4 -> 5

There is also the following code, which is realized through promise.


function add() {
 var counter = 0;
 return function plus() {
 counter += 1;
 return counter
 } 
 }
 var count=add()
 console.log(count())//1
 var counter=100
 console.log(count())//2
0

function add() {
 var counter = 0;
 return function plus() {
 counter += 1;
 return counter
 } 
 }
 var count=add()
 console.log(count())//1
 var counter=100
 console.log(count())//2
1

function add() {
 var counter = 0;
 return function plus() {
 counter += 1;
 return counter
 } 
 }
 var count=add()
 console.log(count())//1
 var counter=100
 console.log(count())//2
2

Closures are used in the above code. In short, closures find the final value of the corresponding variable in the parent function in the same address.

2. Garbage collection mechanism

Memory management in JavaScript is automated and invisible. We create primitive types, objects, functions … all of which require memory.

Generally, there are two methods of garbage collection: flag clearing and reference counting.

1. Mark clearing

When the garbage collector runs, it tags all variables stored in memory. Then, it removes the variables in the environment and the tags referenced by the variables in the environment.

Variables that are tagged later are considered to be ready for deletion because the variables in the environment are no longer accessible.

Finally. The garbage collector completes the memory cleanup, destroying those marked values and reclaiming the memory space they occupy

2. Reference Count

Reference count means tracking the number of times each value is referenced. When a variable is declared and a reference type is assigned to the variable, the number of references to this value is 1.

Conversely, if a variable containing a reference to this value takes another value, the number of references to this value is reduced by 1. When the number of references becomes 0,

It means that there is no way to access this value again, so it can reclaim the memory space it occupies. This way, the next time the garbage collector runs again,

It frees the memory occupied by values referenced 0 times.

Summarize


Related articles: