The operating principle of javascript closure, the role of javascript closure

Hello everyone, the editor will answer your questions about how JavaScript closures operate. Many people still don’t know the role of JavaScript closures, let’s take a look now!

Article directory
    • Closure
      • 1. What is closure?
      • 2. Conditions for generating closure
      • 3. Location of closure variable storage
        • JS stack memory release
      • 4. Common closures
        • Closure 1: Use one function as the return value of another function
        • Closure 2. Passing a function as an argument to another function call
        • Closure 3. Function as parameter
        • Closure 4. IIFE (self-executing function)
        • Closure 5. Loop assignment
        • Closure 6. Throttle and anti-shake
        • Closure 7. Function currying
      • 5. The role of closure
      • 6. Closure life cycle
        • When is the closure destroyed? -Garbage collection mechanism
      • 7. Disadvantages and solutions of closure
      • 8. Memory overflow and memory leak
        • memory overflow
        • memory leak
      • 9. Classic interview questions

Closure

Question:

  1. What is a closure?
  2. What are the practical application scenarios of closures?
  3. How are closures generated?
  4. How are variables generated by closures recycled?
1. What is closure

A closure is a function that has access to variables in the scope of another function. The most common way to create a closure is to create another function within a function. The created function can access the current function. local variables.

In JS, the purpose of closure is to allow us to indirectly access the variables inside the function using the python for statement.

2. Conditions for generating closure

1. Function nesting

2. The internal function refers to the data (variables/functions) of the external function.

PS: Another condition is that the external function is called and the internal function is declared. for example:

 function fn1() {
        var a = 2
        var b = 'abc'

        function fn2() { //The internal function of fn2 is declared in advance, and a closure will be generated (without calling the internal function)
            console.log(a)
        }

    }

    fn1();

    function fn3() {
        var a = 3
        var fun4 = function () { //fun4 uses a function created by "function expression". At this time, the internal function is not declared in advance.
            console.log(a)
        }
    }

    fn3();

3. Location of closure variable storage

Direct explanation:The location where the variables in the closure are stored is heap memory.

  • If the variables in the closure are stored in stack memory, stack recycling will automatically recycle the variables at the top of the stack. Therefore, if the variables in the closure are on the stack, then after the variables are destroyed, the variables in the closure will be gone. So the variables referenced by the closure are in heap memory.
JS stack memory release
  • Heap memory: stores reference type values, object types are key-value pairs, and functions are code strings.
  • Heap memory release: Assign the reference type space address variable to null, or the browser will release this address if no variables occupy the heap memory.
  • Stack memory: Provides an environment for code execution and stores basic type values.
  • Stack memory release: Generally, the private scope of the function will be released after the function is executed.

But there are also special situations in the release of stack memory: ① After the function is executed, but there are contents in the private scope of the function that are still used by variables outside the stack, the basic values in the stack memory cannot be released and will not be released. . ② Global stack memory will only be released when the page is closed

4. Common closures
  • Use a function as the return value of another function
  • Pass a function as an argument to another function call.
Closure 1: Use one function as the return value of another function
 function fn1() {
      var a = 2

      function fn2() {
        a++
        console.log(a)
      }
      return fn2
    }

    var f = fn1(); //Execute external function fn1 and return internal function fn2
    f() // 3 //Execute fn2
    f() // 4 //Execute fn2 again

When f() is executed for the second time, a is increased by 1, which means that the data in the closure has not disappeared, but is saved in the memory. If there is no closure, variable a will disappear after the third to last line of code is executed.

In the above code, although the internal function is called twice, only one closure object is created.

In other words, to see whether a closure object is created, it depends on: How many times the external function is executed (it has nothing to do with how many times the internal function is executed).

Closure 2. Pass the function as an actual parameter to another function call

Using callback functions is using closures

 function showDelay(msg, time) {
      setTimeout(function() { //This function is a closure because it is a nested sub-function and references the variable msg of the external function
        alert(msg)
      }, time)
    }
    showDelay('atguigu', 2000)

In the above code, the closure is the inner function because it is a nested sub-function and refers to the variable msg of the outer function.

Closure 3. Function as parameter
var a = 'cba'
function foo(){
    var a = 'foo'
    function fo(){
        console.log(a)
    }
    return fo
}

function f(p){
    var a = 'f'
    p()
}
f(foo())
/* output
*foo
/

Use return fo to return, fo() is a closure, and the parameter executed by f(foo()) is the function fo, because the superior scope of a in fo() is the function foo(), so the output is foo

Closure 4. IIFE (self-executing function)
var n = 'cba';
(function p(){
    console.log(n)
})()
/* output
*cba
/

The closure p() is also generated, and the reference n under window exists.

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

Closure 6. Throttling and anti-shake
// Anti-shake
function debounce(fn, delay = 300) {
  //default 300 milliseconds
  let timer;
  return function () {
    const args = arguments;
    if (timer) {
      clearTimeout(timer);
    }
    timer = setTimeout(() => {
      fn.apply(this, args); // Change this point to the object pointed to by calling debounce
    }, delay);
  };
}

window.addEventListener(
  "scroll",
  debounce(() => {
    console.log(111);
  }, 1000)
);

// Throttle
//Set a flag
function throttle(fn, delay) {
  let flag = true;
  return () => {
    if (!flag) return;
    flag = false;
    timer = setTimeout(() => {
      fn();
      flag = true;
    }, delay);
  };
}

window.addEventListener(
  "scroll",
  throttle(() => {
    console.log(111);
  }, 1000)
);

Closure 7. Function currying
function currying(fn, ...args) {
  const length = fn.length;
  let allArgs = [...args];
  const res = (...newArgs) => {
    allArgs = [...allArgs, ...newArgs];
    if (allArgs.length === length) {
      return fn(...allArgs);
    } else {
      return res;
    }
  };
  return res;
}

// Usage is as follows:
// const add = (a, b, c) => a + b + c;
// const a = currying(add, 1);
// console.log(a(2,3))

5. The role of closure
  • Function 1. The variables used inside the function will still survive in the memory after the function is executed (extending the life cycle of local variables)

  • Function 2. Allows data (variables/functions) inside the function to be manipulated (read and written) from outside the function

6. Life cycle of closure
  1. Generation: The nested internal function fn2 is generated when it is declared (not called)

  2. Dies: When a nested inner function becomes a garbage object. (For example, if f = null, you can make f a garbage object. This means that f no longer refers to the closure object at this time.

When is the closure destroyed? -Garbage collection mechanism

When is the closure destroyed?

Final conclusion - if there is no special garbage collection algorithm (no such algorithm has been found yet), the closure will become permanent! Unless manually set to null, it will cause a memory leak!

7. Disadvantages and solutions of closures

Disadvantages: After the function is executed, the local variables within the function are not released, and the memory occupied time will become longer, which may easily cause memory leaks.

Solution: If you can't use the closure, don't use it and release it in time. for example:

 f = null; // Let the internal function become a garbage object --> Recycle the closure

In short, if you need it, it is an advantage; if you don't need it, it becomes a disadvantage.

8. Memory overflow and memory leak
Memory overflow

Memory overflow: An error that occurs when a program is running. When the memory required to run the program exceeds the remaining memory, a memory overflow error will be thrown.

Code example:

 var obj = {};
    for (var i = 0; i < 10000; i + + ) {
    obj[i] = new Array(10000000); //Put all array contents into obj and save them, causing obj to occupy a large amount of memory space.
    console.log("-----");
    }
Memory leak

Memory leak: The occupied memory is not released in time.

Note that if the number of memory leaks accumulates too much, it will easily lead to memory overflow.

Common memory leaks:

  • 1. Unexpected global variable

  • 2. There is no timer or callback function for timely cleaning

  • 3. Closure

Case 1 example:

 // Unexpected global variable
    function fn() {
        a = new Array(10000000);
        console.log(a);
    }

    fn();

Case 2 example:

 // There is no timer or callback function for timely cleaning
    var intervalId = setInterval(function () { //Do not clean up after starting the loop timer
        console.log('----')
    }, 1000)

    // clearInterval(intervalId); //Clear timer

Case 3 example:

< type="text/java">
  function fn1() {
    var a = 4;
    function fn2() {
      console.log( + + a)
    }
    return fn2
  }
  var f = fn1()
  f()

  // f = null //Let the internal function become a garbage object-->recycle the closure
</>
9. Classic interview questions
for(var i = 0;i < 5;i + + ) {
  setTimeout(function() {
    console.log(i + + );
  },4000)
}
console.log(i)

Because setTimeout is an asynchronous function, all loops will be executed first. At this time, i is 5, so a bunch of 5 will be output.

  • setTimeout is a macro task. Due to the single-threaded eventLoop mechanism in JS, the macro task is executed only after the main thread synchronization task is executed. Therefore, the callbacks in setTimeout are executed sequentially after the loop ends.

  • Because the setTimeout function is also a closure, its parent scope chain is window. The variable i is a global variable on the window. Before the setTimeout is executed, the variable i is already 5, so the final output sequence is 5.

Solution

  • The first is to use closures

Equivalent to using the immediate execution function (IIFE)

Whenever the for loop is executed, the variable i at this time is passed to the timer, and then executed.

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

explain:

First create a global execution context and enter the for loop. The first round of the loop needs to execute the immediate execution function, so create a context for the immediate execution function. Here i=0, then x=0, because it is asynchronous, the browser puts the processing result into the task queue, that is, puts the value 0 into the task queue. At this time, the context of the immediate execution function is popped from the stack, returns to the global execution context, and enters the second for Loop, at this time i=1, you need to execute the immediate execution function, create the context of the immediate execution function, put the value 1 into the task queue, and the context of the immediate execution function is popped from the stack, and the following three loops Same.

When the global code is executed, the asynchronous code is executed, and the values in the task queue are output in sequence.

  • The second method is to use the third parameter of setTimeout, which will be passed in as a parameter of the timer function.
for (var i = 1; i < 5; i + + ) {
  setTimeout(
    function timer(j) {
      console.log(j)
    },
    4000,
    i
  )
}
  • The third way is to use let to define i to solve the problem. This is also the most recommended way.
for (let i = 1; i <5 5; i + + ) {
  setTimeout(function timer() {
    console.log(i)
  }, 4000)
}
syntaxbug.com © 2021 All Rights Reserved.