Closure issues introduced

  1. JavaScript precompilation and scope chain knowledge tell us that when a function is declared, it generates an array of class properties called [[scope]], and stores the execution-time context of the outer function and the global execution-time context, namely AO and GO, from zero bits forward
  2. When a function accesses a declaration, it looks at bits [[scope]] 0, first at its scope, then at the outer function, and finally at the global context
  3. When an internal function is returned for external use, it generates a closure that holds the scope chain, possibly causing memory leaks or overloads

Closure code example

  1. The inner function retains the AO of the outer function

    function test1() {
        function test2() {
            var b = 2;
            a = 4;
            console.log(a);
        }
        var a = 1;
        return test2;
    }
    var c = 3; / / (1)
    var test3 = test1(); 
    test3(); 
    // Global precompile
    // -> GO{c: undefined, test3: undefinedd, test1: test1(){}}
    // -> test1.SCOPE = [
    // GO{c: undefined, test3: undefinedd, test1: test1(){}}
    / /]
    // execute globally
    / / execution (1)
    // -> GO{c: 3, test: undefined, test1: test1(){}}
    // -> test1.SCOPE = [
    // GO{c: 3, test3: undefined, test1: test1(){}}
    / /]
    Test1 is precompiled by executing test1()
    // -> test1.SCOPE = [
    // test1_AO{a: undefined, test2: test2(){}},
    // GO{c: 3, test3: undefined, test1: test1(){}}
    / /]
    // -> test2.SCOPE = [
    // test1_AO{a: undefined, test2: test2(){}},
    // GO{c: 3, test: undefined, test1: test1(){}}
    / /]
    // function test1 is executed
    // -> test1.SCOPE = [
    // test1_AO{a: 1, test2: test2(){}},
    // GO{c: 3, test3: undefined, test1: test1(){}}
    / /]
    // -> test2.SCOPE = [
    // test1_AO{a: 1, test2: test2(){}},
    // GO{c: 3, test: undefined, test1: test1(){}}
    / /]
    Test1_AO is destroyed when test1 completes execution
    Test1_AO has no relation to test1
    Test2 is returned externally and received with test3
    // -> GO{c: 3, test3: test2(){}, test1: test1(){}}
    // -> test1.SCOPE = [
    // GO{c: 3, test3: test2(){}, test1: test1(){}}
    / /]
    // Execute test3(), i.e. Test2 (), test2 precompiled
    // -> test2.SCOPE = [
    // test2_AO{b: undefined},
    // test1_AO{a: 1, test2: test2(){}},
    // GO{c: 3, test3: test2(){}, test1: test1(){}}
    / /]
    // Function test2 is precompiled
    // -> test2.SCOPE = [
    // test2_AO{b: 2},
    // test1_AO{a: 1, test2: test2(){}},
    // GO{c: 3, test3: test2(){}, test1: test1(){}}
    / /]
    // Function test2 executes, changes the value of b, a, prints 4
    // -> test2.SCOPE = [
    // test2_AO{b: 2},
    // test1_AO{a: 4, test2: test2(){}},
    // GO{c: 3, test3: test2(){}, test1: test1(){}}
    / /]
    Test2_AO is destroyed after test2 is executed
    // -> test2.SCOPE = [
    // test1_AO{a: 4, test2: test2(){}},
    // GO{c: 3, test3: test2(){}, test1: test1(){}}
    / /]
    Copy the code
  2. Sibling inner functions share the AO of external functions

    function cal() {
        var num = 10;
        function plus() {
            num ++;
            console.log(num);
        }
        function minus() {
            num --;
            console.log(num);
        }
        return  [plus, minus];
    }
    var c = cal();
    c[0] (); c[1] (); c[1] ();// Prints 11, 10, 9
    // Analyze the process
    // GO{c: undefined, cal: cal(){}}
    // -> cal.SCOPE = [
    // cal.AO{num: undefined, plus: plus(){}, minus: minus()},
    // GO{c: undefined, cal: cal(){}}
    / /]
    // -> cal.SCOPE = [
    // cal.AO{num: 10, plus: plus(){}, minus: minus()},
    // GO{c: undefined, cal: cal(){}}
    / /]
    // -> GO{c: [plus, minus], cal: cal(){}}
    // cal.SCOPE = [
    // GO{c: [plus, minus], cal: cal(){}}
    / /]
    // -> plus.SCOPE = [
    // plus.AO{},
    // cal.AO{num: 10, plus: plus(){}, minus: minus()},
    // GO{c: [plus, minus], cal: cal(){}}
    / /]
    // -> plus.SCOPE = [
    // plus.AO{},
    // cal.AO{num: 11, plus: plus(){}, minus: minus()},
    // GO{c: [plus, minus], cal: cal(){}}
    / /]
    // -> minus.SCOPE = [
    // minus.AO{},
    // cal.AO{num: 10, plus: plus(){}, minus: minus()},
    // GO{c: [plus, minus], cal: cal(){}}
    / /]
    // -> minus.SCOPE = [
    // minus.AO{},
    // cal.AO{num: 9, plus: plus(){}, minus: minus()},
    // GO{c: [plus, minus], cal: cal(){}}
    / /]
    Copy the code

Closure in a loop

  1. Loop generated inner functions that share the AO of the outer functions

    At the end of the loop, the inner functions are thrown in an array, each of which holds the AO of the test function in [[scope]]

    And this AO is shared. In this AO, I = 10;

    function test() {
    	var arr = [];
        var i = 0; // It has the same effect when placed outside for
        for(; i < 10; i++) {
            arr[i] = function() {
                console.log(i); }}return arr;
    }
    var fnArr = test();
    fnArr[0] (); fnArr[1] (); fnArr[2] (); fnArr[3] ();// Find that all prints are 10
    // test_AO{arr: [...] , i: 10}
    Copy the code
  2. Use immediate execution functions to resolve looping closures

    Immediately-executed functions pass arguments directly to the interior, which means that the interior functions retain the AO of their outer self-executing functions, which hold different js

    function test() {
    	var arr = [];
        var i = 0;
        for(; i < 10; i++) {
            (function(j) {
            	arr[j] = function() {
                	console.log(j);
                }
            })(i);
        }
        return arr;
    }
    var fnArr = test();
    fnArr[0] (); fnArr[1] (); fnArr[2] (); fnArr[3] ();// Print out 0, 1, 2, 3
    Copy the code

Caching features of closures

  1. If the inner function in a closure shares the AO of the outer function, it can share its local variables

    function myClass() {
        var students = [];
        return {
            join: function(name) {
                students.push(name);
            },
            out: function(name) {
                var idx = students.indexOf(name);
                if(idx ! = -1) {
                    students.splice(idx,1); }},show:function() {
                console.log(students); }}}var c = myClass();
    c.join("Zhang");
    c.show();
    c.join("Bill");
    c.show();
    c.out("Bill");
    c.show();
    // [" Zhang SAN "]
    // [" zhang SAN ", "Li Si "]
    // [" Zhang SAN "]
    Copy the code