1. A simple example
First, from a classic error, there are a number of div on the page, we want to bind them an onclick method, and then have the following code
<div id= "Divtest" > <span>0</span> <span>1</span> <span>2</span> < span>3</span> </div> <div id= "DivTest2" > <span>0</span> <span >1</span> <span>2</span> <span>3</span> </div>
$ (document). Ready (function () { var spans = $ ("#divTest span"); for (var i = 0; i < spans.length; i++) { Spans[i].onclick = function () { alert (i);}} );
Very simple function But it is wrong, every alert out of the value is 4, simple modification is good
var spans2 = $ ("#divTest2 span"); $ (document). Ready (function () {for (var i = 0; i < spans2.length; i++) { (function (num) { spans2[i].onclic K = function () { alert (num); } }) (i); } });
2. Intrinsic functions
Let's start with some basic knowledge about the intrinsic functions. An intrinsic function is a function that is defined in another function. For example:
function Outerfn () { functioninnerfn () {}}
INNERFN is an intrinsic function that is wrapped in a OUTERFN scope. This means that calling INNERFN inside the OUTERFN is valid, and calling INNERFN outside of OUTERFN is not valid. The following code causes a JavaScript error:
function Outerfn () { document.write ("Outer function<br/>"); function Innerfn () { document.write ("Inner function<br/>"); } } INNERFN ();
However, if you call INNERFN inside OUTERFN, you can run it successfully:
function Outerfn () { document.write ("Outer function<br/>"); function Innerfn () { document.write ("Inner function<br/>"); } INNERFN (); } OUTERFN ();
2.1 The Great Escape
JavaScript allows developers to pass functions like any type of data, meaning that internal functions in JavaScript can escape the definition of their external functions.
There are many ways to escape, for example, you can assign an intrinsic function to a global variable:
var Globalvar; function Outerfn () { document.write ("Outer function<br/>"); function Innerfn () { document.write ("Inner function<br/>"); } Globalvar = INNERFN; } OUTERFN (); Globalvar ();
When OUTERFN is called, the global variable Globalvar is modified, when its reference becomes INNERFN, and then Globalvar is called and INNERFN is called. Calling INNERFN directly outside the OUTERFN will still result in an error, because the intrinsic function, although escaped by saving the reference in a global variable, still exists only in the scope of the OUTERFN.
You can also get an intrinsic function reference by the return value of the parent function
function Outerfn () { document.write ("Outer function<br/>"); function Innerfn () { document.write ("Inner function<br/>"); } return INNERFN; } var fnref = Outerfn (); Fnref ();
Instead of modifying global variables inside OUTERFN, a reference to INNERFN is returned from OUTERFN. This reference can be obtained by calling OUTERFN, and the reference may be saved in the variable.
The fact that it is possible to invoke an intrinsic function by reference even if it leaves the scope of the function means that JavaScript needs to preserve the referenced function as long as there is the possibility of invoking the intrinsic function. and the JavaScript runtime needs to keep track of all the variables that reference this intrinsic function until the last variable is discarded, and the JavaScript garbage collector frees up the corresponding memory space (the red part is the key to understanding closures).
I've been talking about it for a while. closures are functions that have access to variables of another function scope, and the common way to create closures is to create another function inside one function, which is what we call the intrinsic function, so what we just said is not nonsense, it's a closure-related ^_^
1.2 Scope of variables
Intrinsic functions can also have their own variables, which are limited to the scope of the intrinsic function:
function Outerfn () { document.write ("Outer function<br/>"); function Innerfn () { var innervar = 0; innervar++; document.write ("Inner function\t"); document.write ("Innervar =" +innervar+ "<br/>"); } return INNERFN; } var fnref = Outerfn (); Fnref (); Fnref (); var FnRef2 = Outerfn (); FnRef2 (); FnRef2 ();
Whenever this intrinsic function is invoked by reference or other means, a new Innervar variable is created, then added 1, and the last display
Outer functioninner function Innervar = 1Inner function innervar = 1Outer functioninner function Innervar = 1Inner function Innervar = 1
Intrinsic functions can also refer to global variables like other functions:
var globalvar = 0; function Outerfn () { document.write ("Outer function<br/>"); function Innerfn () { globalvar++; document.write ("Inner function\t"); document.write ("Globalvar =" + Globalvar + "<br/>"); } return INNERFN; } var fnref = Outerfn (); Fnref (); Fnref (); var FnRef2 = Outerfn (); FnRef2 (); FnRef2 ();
The value of this global variable is now continuously incremented for each call to the intrinsic 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 intrinsic functions refer to the scope of the parent function (interested in understanding the scope chain and the knowledge of the active object), intrinsic functions can also refer to these variables
function Outerfn () { var outervar = 0; document.write ("Outer function<br/>"); function Innerfn () { outervar++; document.write ("Inner function\t"); document.write ("Outervar =" + Outervar + "<br/>"); } return INNERFN; } var fnref = Outerfn (); Fnref (); Fnref (); var FnRef2 = Outerfn (); FnRef2 (); FnRef2 ();
This time the results are very interesting, maybe or unexpected.
Outer functioninner function Outervar = 1Inner function outervar = 2Outer functioninner function Outervar = 1Inner function Outervar = 2
What we see is the effect of the previous two-case synthesis, which is called by each reference innerfn to increment the Outervar independently. This means that the second call to OUTERFN does not continue to inherit the value of Outervar, but instead creates and binds a new Outervar instance at the scope of the second function call, which is completely unrelated to the two counters.
When an intrinsic function is referenced outside the scope that defines it, a closure of the intrinsic function is created. In this case, we call either the internal function local variable or the variable of its argument as a free variable, and the calling environment of the external function is a closed-closure environment. In essence, if an intrinsic function references a variable in an external function, it is equivalent to authorizing the variable to be used for delay. Therefore, when the external function call is complete, the memory of these variables is not freed (the last value is saved) and the closures still need to use them.
3. Interaction between closures
When there are multiple intrinsic functions, unexpected closures are likely to occur. We define an increment function, the increment of which is 2.
function Outerfn () { var outervar = 0; document.write ("Outer function<br/>"); function InnerFn1 () { outervar++; document.write ("Inner function 1\t"); document.write ("Outervar =" + Outervar + "<br/>"); } function innerFn2 () { Outervar + = 2; document.write ("Inner function 2\t"); document.write ("Outervar =" + Outervar + "<br/>"); } return {"fn1": InnerFn1, "fn2": InnerFn2}; } var fnref = Outerfn (); Fnref.fn1 (); Fnref.fn2 (); Fnref.fn1 (); var FnRef2 = Outerfn (); Fnref2.fn1 (); Fnref2.fn2 (); Fnref2.fn1 ();
We map a reference that returns two intrinsic functions, and can call any intrinsic function from the returned reference, resulting in:
Outer functioninner function 1 outervar = 1Inner function 2 outervar = 3Inner function 1 outervar = 4Outer Fun Ctioninner function 1 outervar = 1Inner function 2 outervar = 3Inner function 1 outervar = 4
InnerFn1 and InnerFn2 refer to the same local variable, so they share a closed environment. When innerFn1 increments for Outervar, a long absence innerFn2 sets a new starting value for Outervar, and vice versa. We also see that subsequent calls to OUTERFN create new instances of these closures, as well as create a new enclosing environment, essentially creating a new object, which is the instance variable of the object, and the closure is the instance method of the object, and these variables are also private, Because these variables cannot be directly referenced outside the scope in which they are encapsulated, the exclusivity of object-oriented data is ensured.
3. Doubts
Now we can look back at the beginning of the example and it's easy to see why the first one is alert 4 each time.
for (var i = 0; i < spans.length; i++) { Spans[i].onclick = function () { alert (i); } }
The above code will be executed after the page load, when the value of I is 4, the condition is not set, the For loop is executed, but because the onclick method of each span is an intrinsic function, so I is referenced by the closure, memory can not be destroyed, I value will remain 4, It will not be recycled until the program changes it or all of the onclick functions are destroyed (by actively assigning the function null or page unloading). So every time we click on span, the onclick function looks for the value of I (the scope chain is quoted), a check equals 4, and then the alert is given to us. The second way is to use an immediately executed function and create a layer of closure, the function declaration is placed in parentheses into an expression, followed by parentheses and parentheses is called, at this time I when the argument passed, the function is immediately executed, Num saves the value of I.
It must be the same with me, I know about closures, of course, fully understand the function of the execution environment and the scope of the chain to clarify ^_^
What exactly is a JavaScript closure?