Closure issues introduced
- 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
- 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
- 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
-
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
-
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
-
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
-
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
-
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