What is a closure

Closures are created when a variable object holds a reference to the contents of another variable object. The common expression is that the inner function holds variables from the outer function (outer scope), and we can return the inner function so that the outer scope can access variables from the parent function of the inner function.

function foo(){
    var a = 10;
    function fn(){
        a++; // fn holds variable A in the outer scope foo function
        console.log(a);
    }
    return fn;
}
var f = foo();  // The outermost scope gets a reference to fn
f(); / / 11
f(); / / 12
f(); / / 13

var f2 = foo();
f2(); / / 11
f2(); / / 12

Copy the code

After foo() is executed, the variable object in foo’s execution context in the Stack is destroyed. But since the global scoped variable f holds a reference to its internal function fn which in turn holds a reference to variable A. So foo’s execution context variable object is never recycled, and variable A can still be accessed when we call f().

A closure is essentially a reference to an inner function that refers to an object of a variable of an outer function (outer scope)

Why do closures form

The formation of closures requires three conditions:

  • Function nesting is essentially the nesting of function scopes
  • The inner function holds local variables of the outer function
  • External functions are used

Function nesting is well understood because the scope of a function is defined when it is declared, so functions are declared in a nested manner. The inner function holds the external function variable because if the inner function does not hold the external function variable, the inner function will not add the external function variable object to the scope chain when forming the variable object, but will directly pass it. {% post_link ‘2021-11-12-js execution context ‘ ‘js execution context mechanism ‘%} We declared the function and didn’t call it, so it makes sense that the third external function is called.

Closures occur when a condition that meets the above three conditions occurs. But in the real world, internal functions also need to be called or referenced to cause Closure. This is because some browsers optimize internal functions, and debugging internal functions when they are not used or referenced does not result in a Closure object. Because we don’t use internal functions, we don’t have a specific reference during code execution, even though we did it when we defined it, so the browser doesn’t generate a Closure to save memory.

function foo(){
    var a = 10;
    function fn(){
        a++; // fn holds variable A in the outer scope foo function
        console.log(a);
    }
    fn();
}
foo(); / / 11
Copy the code

The above code also generates closures, but we can’t access them more than once compared to the first example. A Closure is generated each time an external function is called

The life cycle of closures

Generated: generated when a nested inner function is defined (not called), because a closure is essentially a reference. When an external function is called, the browser generates a specific Closure

Dead: Closures die when nested internal functions become junk objects, when the reference relationship breaks

The role of closures

Closures extend the life of an external function variable object, and they also make it possible to manipulate variables inside a function indirectly. Although closures can preserve variable objects from external functions, browsers later remove variables from external functions that are not used by internal functions for performance purposes.

function foo(){
    var a = 10;
    var b = 20;
    function fn(){
        console.log(a);  // Use only the variable a
    }
    return fn;
}
var f = foo();
f(); / / 10
Copy the code

Disadvantages of closures

Because of closures, variable objects of external functions are not destroyed immediately after execution. This leads to a memory leak, and the variable object will remain in memory until it is released manually. When there is too much memory leakage, the page becomes more and more jammed, and eventually the program runs out of memory and crashes. The solution to this problem is simply to set the variable holding the reference to NULL and break the reference relationship so that the GC can reclaim the memory normally.

Application of closure

Js modularity is the use of closures, using it to prevent global variable pollution. Multiple modules are introduced without overwriting the same variable name.

Another classic use is to have closures store index numbers

<! Click on each li to print the position of its array subscript -->
<body>
    <ul>
        <li>0</li>
        <li>Output 1</li>
        <li>The output of 2</li>
        <li>The output of 3</li>
    </ul>
<script>
    var btns = document.querySelectorAll('li');
    for(var index = 0; index<btns.length; index++){// Use closures to store index values passed in externally
        (function(i){
            btns[i].onclick = function(){
                console.log(i);
            }
        })(index);
    }
</script>
Copy the code

There is another solution to the above example, which is to use the let keyword to bind block-level scope to achieve the effect of closure saving variables

// Just change var index in the for loop to let index
var btns = document.querySelectorAll('li');
for(let index = 0; index<btns.length; index++){ btns[index].onclick =function(){ console.log(index); };
}
Copy the code

The Block stores the index value and prints the saved index value when the click event is triggered

Self-test questions

// Code snippet 1
var name = "The Window";
var object = {
    name: "My Object".getNameFunc: function () {
        return function () {
            return this.name; }; }};console.log(object.getNameFunc()());  

// Code snippet 2
var name2 = "The Window";
var object2 = {
    name2: "My Object".getNameFunc: function () {
        var that = this;
        return function () {
            returnthat.name2; }; }};console.log(object2.getNameFunc()());	


// Snippet three
function fun(n, o) {
    console.log(o)
    return {
        fun: function (m) {
            return fun(m, n)
        }
    }
}
var a = fun(0) // undefined
a.fun(1)  // undefined
a.fun(2) // undefined
a.fun(3) // undefined
var b = fun(0).fun(1).fun(2).fun(3)  
Copy the code