I want to learn about the closure of javascript. The purpose of this article is to give you a comprehensive understanding of the closure of javascript. If you are interested, you can refer to it.
What is JavaScript closure?
After using JavaScript for more than a year, the closure is always confusing. I have been familiar with some closures and made several mistakes due to my incomprehension of closures. I have read more information over a year, but I still don't quite understand it, recently I accidentally read the appendix of the basic jQuery tutorial and found that appendix A's introduction to JavaScript closures is easy to understand.
1. Definition
Closure: a function that has the right to access variables in another function scope. A common way to create a closure is to create another function within a function.
Example
function a(){ var i=0; function b(){ alert(++i); } return b;}var c = a();c();
This code has two features:
1) function B is nested inside function;
2) function a Returns function B.
In this way, after var c = a () is executed, variable c actually points to function B and then executes c () then a window will pop up showing the I value (the first time is 1 ). This Code actually creates a closure. Why? Because variable c outside function a references function B in function a, that is:
When function a's internal function B is referenced by a variable outside function a, a closure is created.
I guess you still don't understand the closure, because you don't know what the closure will do. Let's continue exploring it.
2. What is the function of closure?
In short, the function of the closure is that after a executes and returns, the closure makes the garbage collection mechanism of Javascript GC not to reclaim the resources occupied by, because the execution of the internal function B of a depends on the variables in. This is a straightforward description of the function of the closure, which is neither professional nor rigorous, but probably means that the process of understanding the closure needs to be gradual.
In the above example, because of the existence of the closure, the I in a always exists after function a returns, so that each execution of c (), I is the value of alert after auto-increment 1.
Then let's imagine another situation. If a does not return function B, the situation is completely different. Because after a is executed, B is not returned to the external world of a, but is referenced by a. At this time, a will only be referenced by B, therefore, functions a and B are referenced by each other without being disturbed by the outside world (referenced by the outside world), and functions a and B are recycled by GC. (The garbage collection mechanism of Javascript will be described in detail later)
3. microview of the world in the closure
To learn more about closures and the relationship between function a and nested function B, we need to introduce several other concepts: the function execution environment (excution context), and the activity object (call object) scope and scope chain ). The process from definition to execution of function a is used as an example to describe these concepts.
1) when defining function a, the js interpreter sets the scope chain of function a to the "Environment" of function a when defining function ", if expression a is a global function, the scope chain contains only window objects.
2) When function a is executed, function a enters the corresponding execution environment (excution context ).
3) in the process of creating the execution environment, a scope attribute, that is, the scope of a, will be added for a, and its value is the scope chain in step 1. That is, the scope chain of a. scope =.
4) then, the execution environment will create a call object ). An activity object is also an object with attributes, but it does not have a prototype and cannot be directly accessed through JavaScript code. After creating the activity object, add the activity object to the top of the scope chain of. In this case, the scope chain of a contains two objects: The activity object of a and the window object.
5) The next step is to add an arguments attribute to the activity object, which stores the parameters passed when calling function.
6) Finally, add the parameters of all function a and reference of internal function B to the activity object of function. In this step, the definition of function B is completed. As in step 1, the scope chain of function B is set to the environment defined by B, that is, the scope of function.
At this point, the steps from definition to execution of function a are completed. At this time, a returns the reference of function B to function c, and the scope chain of function B contains the reference to the activity object of function, that is to say, B can access all variables and functions defined in. Function B is referenced by Function c, and function B is dependent on function a. Therefore, function a is not recycled by GC after return.
When function B is executed, it will be the same as the above steps. Therefore, during execution, B's scope chain contains three objects: B's activity object, a's activity object, and window object. When function B accesses a variable, the search order is to first search for its own activity objects. If it exists, it will be returned. If it does not exist, it will continue to search for the activity objects of function a until they are found. If the entire scope chain cannot be found, undefined is returned. If function B has a prototype object, search for its own prototype object after searching for its own activity object. This is the Variable Search Mechanism in Javascript.
4. Closure application scenarios
1) protect the security of variables in the function. Taking the initial example as an example, in function a, I can only access function B, but cannot access function B through other channels, thus protecting the security of I.
2) maintain a variable in the memory. Still, for example, because of the closure, the I in function a is always in the memory, so each execution of c () will add 1 to the I self.
The above two points are the most basic application scenarios of closures. Many classic cases are based on this.
5. Garbage collection mechanism of Javascript
In Javascript, if an object is no longer referenced, the object will be recycled by GC. If two objects are referenced by each other and no longer referenced by 3rd, the two objects referenced by each other will be recycled. Because function a is referenced by function B and function B is referenced by Function c outside of function a, this is why function a is not recycled after execution.
There is no block-level scope in javascript. Generally, to declare some local variables that can only be used by a function, we will use the closure, in this way, we can greatly reduce the variables in the global scope and purify the global scope.
The advantages of using a closure are as follows. Of course, this benefit requires a price. The price is the memory usage.
How can we understand the above sentence?
Each function execution creates a function execution environment related to the function, or a function execution context. In this execution context, there is an attribute scope chain (scope chain pointer). This pointer points to a scope chain structure, and all the pointers in the scope chain point to the activity objects corresponding to each scope. Normally, a function creates the execution context and corresponding scope chain when the call starts to execute the function. After the function execution ends, the execution context and the space occupied by the corresponding scope chain are released.
// Declare the function test () {var str = "hello world"; console. log (str) ;}// call the function test ();
The following structure is generated in the memory when a function is called:
However, the closure is a little special. Because the closure function can access the variables in the outer function, after the execution of the outer function, its scope activity object will not be released (note, after the execution of the outer function ends, the execution environment and corresponding scope chain will be destroyed.) instead, the execution environment will be referenced by the scope chain of the closure function until the closure function is destroyed, the scope activity object of the outer function will be destroyed. This is exactly why the closure needs to occupy memory.
Therefore, the use of closures has advantages and disadvantages. Misuse of closures will cause a large amount of memory consumption.
The use of closures has other side effects, such as bugs or failures. Different services may have different opinions.
This side effect is that the closure function can only get the final value of the outer function variable.
The test code is as follows: (jquery object is used here)
/* Closure defect */(function ($) {var result = new Array (), I = 0; for (; I <10; I ++) {result [I] = function () {return I ;}}$. RES1 = result;}) (jQuery); // execute the functions in the array $. RES1 [0] ();
The code above first opens up a private scope through an anonymous function expression. This anonymous function is the outer function we mentioned above. This outer function has a parameter $, at the same time, the variables result and I are also defined, and an anonymous function is assigned to the array result through the for loop. This anonymous function is the closure, which accesses the variable I of the outer function, theoretically, the array resulti will return the corresponding array tag value, but the actual situation is not as expected.
The execution result of the Code $. RES10 above is 10.
Why? Because the final value of I is 10.
Next we will explain in detail what happened in the memory during the above code execution:
Is there any way to fix this side effect? Of course!
We can use the following code to achieve our expectations.
/* Fix closure defects */(function ($) {var result = new Array (), I = 0; for (; I <10; I ++) {result [I] = function (num) {return function () {return num ;}} (I) ;}$. RES2 = result;}) (jQuery); // call the closure function console. log ($. RES2 [0] ();
What happened to the above Code in the memory? We also use the following figure for detailed explanation. After reading the figure above, we can easily understand the figure below.
6. Simple Example
First of all, from a classic error, there are several p on the page. We want to bind them with an onclick method, so we have the following code
0 1 2 3
0 1 2 3
$(document).ready(function() { var spans = $("#pTest span"); for (var i = 0; i < spans.length; i++) { spans[i].onclick = function() { alert(i); } }});
A very simple function is wrong. Every time alert outputs a value of 4, it is easy to make a simple modification.
var spans2 = $("#pTest2 span");$(document).ready(function() { for (var i = 0; i < spans2.length; i++) { (function(num) { spans2[i].onclick = function() { alert(num); } })(i); }});
7. Internal functions
Let's start with some basic knowledge. First, let's take a look at internal functions. An internal function is a function defined in another function. For example:
function outerFn () { functioninnerFn () {}}
InnerFn is an internal function in the outerFn scope. This means that calling innerFn inside outerFn is valid, while calling innerFn outside outerFn is invalid. The following code causes a JavaScript error:
function outerFn() { document.write("Outer function
"); function innerFn() { document.write("Inner function
"); }}innerFn();//Uncaught ReferenceError: innerFn is not defined
However, if innerFn is called within outerFn, the operation can be successful:
function outerFn() { document.write("Outer function
"); function innerFn() { document.write("Inner function
"); } innerFn(); } outerFn();
8. Great Escape (How can internal functions escape external functions)
JavaScript allows developers to pass functions like passing any type of data. That is to say, internal functions in JavaScript can escape defining their external functions.
There are many ways to escape. For example, you can specify an internal function to a global variable:
// Define global variables to escape var globalVar; function outerFn () {document. write ("Outer function
"); Function innerFn () {document. write (" Inner function
");} GlobalVar = innerFn;} outerFn (); // Outer function Inner functionglobalVar (); // Outer function Inner functioninnerFn (); // ReferenceError: innerFn is not defined
When outerFn is called, the global variable globalVar is modified. At this time, its reference is changed to innerFn. After that, globalVar is called and innerFn is called. At this time, calling innerFn directly outside outerFn will still cause errors, because although the internal function can escape by saving the reference in the global variable, however, the name of this function still exists only in the scope of outerFn.
You can also obtain internal function references by returning values from the parent function.
function outerFn() { document.write("Outer function
"); function innerFn() { document.write("Inner function
"); } return innerFn;}var fnRef = outerFn();fnRef();
The global variable is not modified in outerFn, but a reference to innerFn is returned from outerFn. You can obtain this reference by calling outerFn, and the reference can be saved in the variable.
The fact that the internal function can still be called by referencing even if the function scope is left exists means that JavaScript needs to retain the referenced function as long as the internal function is called. In addition, when JavaScript is running, it is necessary to trace all the variables that reference this internal function. Until the last variable is discarded, the JavaScript Garbage Collector can release the corresponding memory space (the red part is the key to understanding the closure ).
After talking about the closure for a long time, it is related to the closure. The closure refers to a function with the permission to access the variables of another function scope. A common way to create a closure is to create another function within a function, it is the internal function we mentioned above, so what we just talked about is not nonsense, but also the closure-related javas_^.
9. Scope of Variables
Internal functions can also have their own variables, which are restricted to the scope of internal functions:
function outerFn() { document.write("Outer function
"); function innerFn() { var innerVar = 0; innerVar++; document.write("Inner function\t"); document.write("innerVar = "+innerVar+"
"); } return innerFn;} var fnRef = outerFn(); fnRef(); fnRef(); var fnRef2 = outerFn(); fnRef2(); fnRef2();
Whenever this internal function is called by reference or other means, a new innerVar variable is created, followed by 1, and finally displayed.
Outer functionInner function innerVar = 1Inner function innerVar = 1Outer functionInner function innerVar = 1Inner function innerVar = 1
Internal functions can also reference global variables like other functions:
var globalVar = 0;function outerFn() { document.write("Outer function
"); function innerFn() { globalVar++; document.write("Inner function\t"); document.write("globalVar = " + globalVar + "
"); } return innerFn;} var fnRef = outerFn(); fnRef(); fnRef(); var fnRef2 = outerFn(); fnRef2(); fnRef2();
Now the value of this global variable is continuously increasing every time you call an internal function:
Outer functionInner function globalVar = 1Inner function globalVar = 2Outer functionInner function globalVar = 3Inner function globalVar = 4
But what if this variable is a local variable of the parent function? Because internal functions reference the scope of the parent function (if you are interested, you can learn about the scope chain and activity object), internal functions can also reference these variables.
function outerFn() { var outerVar = 0; document.write("Outer function
"); function innerFn() { outerVar++; document.write("Inner function\t"); document.write("outerVar = " + outerVar + "
"); } return innerFn;}var fnRef = outerFn();fnRef();fnRef();var fnRef2 = outerFn();fnRef2();fnRef2();
This time the results were very interesting and perhaps unexpected.
Outer functionInner function outerVar = 1Inner function outerVar = 2Outer functionInner function outerVar = 1Inner function outerVar = 2
We can see the effect of merging in the previous two cases. Calling innerFn through each reference will independently increase outerVar. That is to say, the second call of outerFn does not continue to use the value of outerVar, but creates and binds a new outerVar instance in the scope of the second function call. The two counters are completely irrelevant.
When an internal function is referenced outside its scope, a closure of the internal function is created. In this case, we call neither a local variable of an internal function nor a variable of its parameter as a free variable, and call an environment of an external function as a closed closure. Essentially, if an internal function references a variable in an external function, it is equivalent to authorizing the variable to be delayed. Therefore, when the external function is called, the memory of these variables will not be released (the last value will be saved), and closure still needs to use them.
10. Interaction between closures
When multiple internal functions exist, unexpected closures may occur. We define an incremental function. The increment of this function is 2.
function outerFn() { var outerVar = 0; document.write("Outer function
"); function innerFn1() { outerVar++; document.write("Inner function 1\t"); document.write("outerVar = " + outerVar + "
"); } function innerFn2() { outerVar += 2; document.write("Inner function 2\t"); document.write("outerVar = " + outerVar + "
"); } return { "fn1": innerFn1, "fn2": innerFn2 };}var fnRef = outerFn();fnRef.fn1();fnRef.fn2();fnRef.fn1();var fnRef2 = outerFn();fnRef2.fn1();fnRef2.fn2();fnRef2.fn1();
Ing returns the reference of two internal functions. You can call any internal function through the returned reference. The result is as follows:
Outer functionInner function 1 outerVar = 1Inner function 2 outerVar = 3Inner function 1 outerVar = 4Outer functionInner function 1 outerVar = 1Inner function 2 outerVar = 3Inner function 1 outerVar = 4
InnerFn1 and innerFn2 reference the same local variable, so they share a closed environment. When innerFn1 is an incremental value of outerVar, a new start value of outerVar is set for innerFn2, and vice versa. We can also see that subsequent calls to outerFn will also create new instances of these closures, and will also create a new closed environment, essentially creating a new object, free variables are the instance variables of this object, and closures are the instance methods of this object, and these variables are also private, because they cannot be directly referenced outside the scopes that encapsulate them, this ensures the uniqueness of object-oriented data.
11. Confused
Now we can look back at the example at the beginning to easily understand why alert 4 is used in the first writing method every time.
for (var i = 0; i < spans.length; i++) { spans[i].onclick = function() { alert(i); }}
The code above will be executed after the page is loaded. When the value of I is 4, the judgment condition is not true and the for loop is executed completely, however, because the onclick method of each span is an internal function at this time, I is referenced by the closure (the closure is referenced), the memory cannot be destroyed, and the I value will always be 4, it will not be recycled until the program changes it or all the onclick functions are destroyed (the function is automatically assigned null or the page is uninstalled. In this way, every time we click span, The onclick function will look for the value of I (the scope chain is referenced), check that it is equal to 4, and then alert will give it to us.
The second method is to use a function that is executed immediately and create a closure. When the function declaration is placed in parentheses, it becomes an expression, and the brackets are added to the brackets to call the function, when I is passed in, the function is executed immediately, and num stores the value of I each time.
I think everyone knows about the closure as well. If you fully understand it, you need to clarify the execution environment and scope chain of the function.