Javascript closure truth

Source: Internet
Author: User

This articleArticleI tried my best to explain the closures in Js. I would like to express my gratitude to the people who love and write well. I have benefited a lot from this. However, this time I want to write this story in another way, that is, to answer questions. I will first ask the questions I want to answer, if you are confident enough to answer questions, you can consider doing other things. If you feel a little unsure, please give me step-by-step addressing. :) In the end, I promise you will be enlightened and understand the true meaning of the closure. Problem set:

    1. What is a function instance?
    2. What is function reference?
    3. What is a closure?
    4. What's in the closure?
    5. What is the relationship between function instances, function references, and closures?
    6. What is the occurrence of closures?
    7. What is the priority of the identifier in the closure?
    8. The visibility problem caused by closures.

 

What is a function instance?In fact, we usually writeCodeIn the process, the written function is a piece of text, but for the compiled language, it will compile it into a fixed binary code and put it in a fixed memory location for execution, for interpreted languages such as JSProgramWhen running, translate the text into something that the computer can understand. The text of the function code here is actually the class of this function, and the data in the memory after the JS engine interpretation is an example of this function. SoThe function instance is a piece of data generated in the memory after the JS engine reads this code..

What is function reference?Function reference refers to the pointer to the memory data just mentioned. That is, the pointer to a function instance. The same instance can have multiple references. As long as the instance still has a reference pointing to it, the occupied memory will not be released.

What is a closure?A closure is a new data set in memory dynamically generated during execution of a function instance.. This sentence can express two meanings:

    • The closure must be dynamically generated by the JS engine only when the function instance is called (that is, the function instance is executed;
    • Since the closure is also a memory area, it will be released only when it is not dependent on the reference of data in the memory, that is, the closure will be destroyed theoretically.

Remember: closure is the concept of execution period!

What's in the closure?Here I will introduce two types of structure constructed by the JS engine during the syntax analysis period. Execute context and callobject ). Let's take a look at an image first:

From the above picture, we can easily see that this is a map describing the information of a function instance. The context structure contains the types, names (myfunc), formal parameters (x, y, z), and references to the most critical callobject structure (the body is callobject) of myfunc functions ). The callobject structure records all the syntax analysis structures of the function in detail: the declared variable table (localdeclvars) and the internally declared named function table (localdeclfuncs) and the string representation (source) of all other code texts except the preceding content ).

Well, now we have the callobject structure generated during this syntax analysis period, so we can analyze what will happen in the closure. When a function instance is called, a callobject structure is actually copied from this prototype to the empty memory of the closure. It also assigns values to the data. There are two rules to note:

    1. When the function instance starts execution, all values in localdeclvars will be set to undefined;
    2. Localdeclvars is not reset when the function instance ends and exits. This is why the function in JS can save data internally;
    3. How long the data in the callobject structure in this closure can be retained depends on whether there are still references to it. Therefore, here is a deduction,The lifecycle of a closure does not depend on the function instance..

What about global objects? What is the structure? In fact, it is basically the same as the above function instance. The only difference is that there is no context, because it is at the top layer. There is also a callobject in the global object, but this callobject is a bit special, that is, the data in localdeclvars will only be initialized once, and the data in the whole callobject will not be destroyed.

In addition to copying a callobject and assigning values to it, the closure Actually includes a upvalue array, here, the array contains references to the identifiers (localdeclvars and localdeclfuncs and their own upvalue arrays) in the top-level closure chain of the closure chain.

What is the relationship between function instances, function references, and closures?To answer such a question, you must understand what the three mentioned above respectively refer. Then I want to perceive something through code before answering this question. In fact, sometimes people's thinking needs a carrier to rely on, that is, the practice we often say can be truly known.

[JavaScript] function myfunc () {This. dofunc = function () {}} var OBJ ={}; // enter myfunc and obtain an instance of dofunc () myfunc. call (OBJ); // gets a reference of the function instance and assigns it to funcvar func = obj. dofunc; // enter myfunc again, and get a new instance of dofunc () myfunc. call (OBJ); // compare the function instances obtained twice. The result is false, indicating that the function instances are different print (func = obj. dofunc); // display false // display true, indicating that the Code text of the two function instances is exactly the same as print (func. tostring () = obj. dofunc. tostring (); // display true [/JavaScript]

I called myfunc twice, but I found that different instances (FUNC AND obj. dofunc) are generated for the same code text ). Why? In fact, every time we call the myfunc function, the JS engine enters the myfunc function. When the value assignment operation is completed, the Code text of the anonymous function must be explained. Therefore, a function instance is generated based on it. After that, let the dofunc Member of the OBJ object point to this instance. After multiple calls, values are assigned to myfunc multiple times, and a new example of the same Code text is generated multiple times. dofunc members refer to different function instances (but the static code texts of the function instances are identical. Here they are all anonymous empty functions ). Let's look at an example and reference:

 
[JavaScript] function myobject () {} myobject. prototype. method = function () {}; var obj1 = new myobject (); var obj2 = new myobject (); print (obj1.method === obj2.method ); // display true [/JavaScript]

The obj1 and obj2 methods here are actually references to a function instance, but how can they be the same? In fact, let's look back at what I said in the JS object truth. prototype is an object instance that maintains its own member table, and the object instance of myobject has a pointer pointing to this member table, instead of copying one copy. Therefore, both obj1.method and obj2.method actually execute two references to the same function instance. Again:

 
[JavaScript] var outfunc = function () {// closure 1 var myfunc = function () {}; return function () {// closure 2 Return myfunc ;}}(); // note the call operation var F1 = outfunc (); var F2 = outfunc (); print (F1 = F2); // display true [/JavaScript]

This example is actually a visual trap. If you do not pay attention to the () Number of the function call, the previous knowledge will deduce that the instances referred to by F1 and F2 are not the same (although their code texts are the same ). But what I want to talk about in this example is about the upvalue array. I will not forget this array. Outfunc eventually becomes an anonymous function (Function () {return myfunc}) Instance reference. Because the data in localdeclfuncs in closure 1 is referenced, closure 1 will not be destroyed. Then the instance that calls this outfunc function instance returns the instance of myfunc function, but only one, that is, the same instance is returned for multiple calls. The reason is that the anonymous FunctionFunction () {return myfunc}The instance actually finds myfunc (located in closure 1) Through upvalue in closure 2 generated during its running, and myfunc should be paid special attention to in my () the operation is generated and recorded in localdeclvars of closure 1.

The previous function instances correspond to a closure, here I want to show a function instance that can correspond to multiple closure examples to pave the way for answering function instances, function references, and closures.

 [JavaScript] var globalfunc; function myfunc () {// closure 1 If (globalfunc) {globalfunc ();} print ('Do myfunc: '+ Str ); vaR STR = 'test'; If (! Globalfunc) {globalfunc = function () {// closure 2 print ('Do globalfunc: '+ Str) ;}} return arguments. callee;} myfunc ();/* output result: Do myfunc: Undefined // do globalfunc: Test // do myfunc: undefined // displayed at the second execution */[/JavaScript] 

This example is a bit complicated, so readers can analyze it calmly. The myfunc function instance is called twice, but both are the same myfunc instance. This is because after the myfunc instance is called for the first time, a reference is returned.Return arguments. callee). Then, it is immediately operated by () to complete the second call. In the first call, STR has not been assigned a value, but has been declared and initialized to undefined, so it is displayedDo myfunc: Undefined. The subsequent statement implements the value assignment. At this time, the STR value is "test". Because globalfunc is undefined, it is assigned as a reference to an anonymous function instance. Because globalfunc is a global variable, it is a global closure that will not be destroyed. Therefore, after the first myfunc function instance is called, the anonymous function instance in globalfunc in the global closure references the STR (value: Test) in closure 1, so that closure 1 will not be destroyed, when the second myfunc function instance is called, a new closure 3 is generated, and the variable STR is first declared and initialized to undefined. Because the global variable globalfunc has a value, it will directly call the anonymous function instance in the corresponding closure 1 to generate the closure 2. At the same time, the closure 2 obtains the STR in the closure 1 through the upvalue array, in closure 1, it is "test" at the time, so the output isDo globalfunc: Test. Note: This shows that a new closure is generated for multiple calls to the same instance, and the data in the closure is not shared.

In general, this example reflects several problems:

    • The same instance in JS may have multiple closures;
    • In JS, the life cycles of function instances and closures are managed separately;
    • When a function instance is called in JS, a new closure is always generated, but whether the closure generated in the last call has been destroyed depends on whether the closure contains data referenced by other closures.

At this point, it is time to reveal the mysteries.Function instances can have multiple function references. If a function instance is referenced, the instance will not be destroyed. Closures are generated when a function instance is called, but they are not necessarily destroyed after the call ends. A function instance can have multiple closures at the same time.

What are the causes of closures?In fact, the segments can be divided:

    1. Global closure;
    2. Closure generated by the name function instance;
    3. Closures generated by anonymous function instances;
    4. PassNew Function (bodystr)Closure of the generated function instance;
    5. The closure of the object indicated by the with statement.

Don't worry about the next three types. I will give an example of the visibility problem caused by the closure below.

What is the priority of the identifier in the closure?To understand the priority, we must first talk about the identifier in the closure. To put it completely, the identifier system in the closure of the function instance includes:

    1. This
    2. Localdeclvars
    3. Form parameters of function instances
    4. Arguments
    5. Localdeclfuncs

Let's first look at an example:

 
[JavaScript] function foo1 (arguments) {print (typeof arguments) ;}foo1 (1000); // displays numberfunction arguments () {print (typeof arguments);} arguments (); // display objectfunction foo2 (foo2) {print (foo2);} foo2 ('Hi'); // display hi [/JavaScript]

From the above analysis, it is not difficult to find that the form parameter takes precedence over arguments (known by foo1), and the built-in object arguments takes precedence over the function name (known by arguments ), the last one reflects that the formal parameter is better than the function name. In fact, the first two also illustrate this point.The format parameter is better than the arguments parameter than the function name.. Let's look at another example:

[JavaScript] function Foo (STR) {var STR; print (STR) ;}foo ('test'); // display testfunction foo2 (STR) {var STR = 'not test'; print (STR);} foo2 ('test'); // display not test [/JavaScript]

In this example, we can draw a conclusion:

    1. When the formal parameter name is the same as the unassigned local variable name, the formal parameter is used;
    2. When the formal parameter name is the same as the value of a local variable, take the local variable value.

However, we cannot use this keyword as a function name or form parameter, so there is no conflict. It can be considered as the highest priority.

Visibility issues caused by closuresThis is not a question, but a practice for understanding closures. The reason for this is that many of the identifier visibility problems we encounter are actually closely related to closures. For example, an internal function can access the identifier of an external function because the closure of an internal function instance uses its upvalue array to obtain the data in the closure of an external function instance. The essence of visibility coverage is that the corresponding identifier can be found in the closure of the internal function instance, and the identifier in the upper-level closure will not be searched through the upvalue array. For example, if we do not use the VaR keyword to declare an identifier in a function, this identifier is implicitly declared globally. In fact, this is because no corresponding identifier can be found in all the closures of the function instance, and it has never been in the global closure, so the JS engine is in the global closure (this closure is a bit special) this identifier is declared as a fault tolerance for your code, so it is best not to use it like this, and it is easy to pollute the identifier system of global closures, you must know that global closures will not be destroyed.

Here I want to add a very important topic, that is, how to determine the position of the closure in the closure chain?

    1. There is nothing to say about global closure, and it must be the top layer;
    2. For the closure generated by the named function instance,Static syntax ScopeDetermine the syntax scope in which the JS engine performs syntax analysis. The resulting closure will also be in this order in the closure chain, this is because the function instance will also be executed at this location when it is executed. That is, if it is represented as a function in the Code text, its closure will be wrapped by the closure generated by the external function instance in the future. Note:Spidermonkey is a bit different. The function closure chain in the with statement is affiliated with the closure opened by the with statement.This will be an example later.
    3. The closure location of an anonymous function instance is determined by the location where the anonymous function is directly created. It is dynamically added to the closure chain, and is irrelevant to whether it has been executed, the direct volume of anonymous functions will still return to the creation location for execution;
    4. PassNew Function (bodystr)The closure of the generated function instance is very interesting. No matter where it is created, it is always directly attached to the global closure, that is, the data in its upvalue array is the data in the global closure;
    5. The closure position of the object indicated by the with statement is determined by the specific position of the with statement during execution, and is dynamically added to the closure chain.

Let's look at some examples to illustrate the above conclusion.

[JavaScript] VaR value = 'Global value'; function myfunc () {VaR value = 'local value'; var Foo = new function ('print (value )'); foo ();} myfunc (); // display global valuevar OBJ ={}; var events = {M1: 'clicked', M2: 'changed '}; for (E in events) {OBJ [e] = function () {print (events [e]) ;}} obj. m1 (); // display the same obj. m2 (); // display the same var OBJ ={}; var events = {M1: 'clicked', M2: 'changed '}; for (e in events) {OBJ [e] = new function ('print (events ["'+ E +'"]) ');} obj. m1 (); // display clickedobj. m2 (); // display changed [/JavaScript]

The preceding code can be divided into three parts. The first part illustratesNew Function (bodystr)The position of the closure in the closure chain of the generated function instance is always directly affiliated to the global closure, rather than the closure of myfunc. The second section shows the same reason: when M1 and M2 are called, their respective closures must reference E in the global closure, E is the index of the last element in the for iteration of the events array. The third section only uses the new function to replace the original function. This changes because the function constructor imports strings and will not reference the global closure e in the future.

If we understand the visibility of the closure as the upvalue array of the closure and the identifier system in the closure, in general, the closure of the function instance is the same as that of the object indicated by the with statement on the former, and the processing on the latter is different. The reason is: the closure of the object indicated by the with statement is accessible only by the object member name, but does not have this, function form parameter, built-in object arguments that automatically creates a value assignment, and localdeclfuncs, for the effect of VaR declaration variables in the closure, there will be some differences in the specific engine implementation. I think this code should be avoided, so I will not discuss it here. Let's look at some Code related to the with statement:

 
[JavaScript] var X; with (x = {x1: 'x1 ', X2: 'x2'}) {X. x3 = 'x3'; // use the global variable X to access an anonymous object for (var I in X) {print (X [I]);} function Self (X) {return X. self = x ;}with (self ({x1: 'x1 ', X2: 'x2'}) {self. x3 = 'x3'; // access the object itself through self, a member of the anonymous object itself. For (var I in self) {if (I! = 'Self ') print (Self [I]);} [/JavaScript]

The comments in the above Code are clearly written, so I will not say much. Now let's look at a serious problem:

 
[JavaScript] var OBJ = {v: 10}; var v = 1000; with (OBJ) {function Foo () {v * = 3;} Foo ();} /* With (OBJ) {obj. foo = function () {v * = 3;} obj. foo ();} alert (obj. v); // display 30 alert (V); // display 1000 */Alert (obj. v); // display 10 alert (V); // display 3000 [/JavaScript]

This code is represented as 10 3000 in IE8, safari3.2, opera9, and chrome1.0.154.48, but changed to 30 1000 in firefox3.0.7, which is very special and may be a bug. This is against the general situation where the closure of the named function instance is located in the closure chain, that is, the syntax analysis period is determined, and the with statement is transparent. In fact, from the first impression of the Code, it seems that the property V of the OBJ object is multiplied by 3, rather than the global variable v. In fact, except for firefox3.0.7 (FF of other versions has not been tested), the closure position of the foo function is determined during the syntax analysis period and is directly affiliated to the global closure, the execution position of the closure of the OBJ object opened by the with statement determines that it is also directly affiliated with the global closure, so the parallel execution occurs, the multiplication 3 in the foo function naturally cannot access the object obj in the with closure. v, so it is displayed as 10 3000. If you want to display them as 30 and 1000, you can use the dynamic feature of the closure location of the anonymous function instance to determine the position of the closure in the closure chain by directly creating the number of locations. Therefore, the code I commented on above can be displayed as 30 and 1000, and there is no problem with Firefox. Note that the closure location of an anonymous function instance has nothing to do with whether it has been executed. The following code describes this:

 
[JavaScript] function Foo () {function foo2 () {// closure of foo2 var MSG = 'hello'; M = function (varname) {// when the volume is directly created and assigned to m, it determines that the closure of this anonymous function instance [future] belongs to the closure of foo2, therefore, you can use the upvalue array to access MSG return eval (varname) ;}} foo2 (); var aformatstr = 'the value is :$ {MSG} '; var m; vaR RX =/\ $ \{(. *?) \}/G; print (aformatstr. replace (RX, function ($0, varname) {return M (varname) ;})} Foo (); // display Hello [/JavaScript]

Okay, I have finished talking about it. In short,Closure is the concept of execution period. If you have any questions about the closure or have any comments about this article, you are welcome to leave a message, comment, or directly email me.

[Excerpt: http://hjp.im/javascript/master-javascript-closure/]

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.