You don't know. JS (2) in-depth knowledge of closures (very important)

Source: Internet
Author: User
Tags closure

A long time ago wanted to write a blog about closures, but always worried about not enough to write completely, not good enough, anyway, or to my understanding of the closure and everyone to share under, longer, hope to read patiently.

Defined

To be honest, it is difficult to define the next definition of closure, because JavaScript is designed without a rule that specifically designs closures, which are born with the rules of scope chains and functions that can be first-class citizens.

Although it is not possible to make a perfect definition, we can still give the next best definition of closure.

Closures: When a function can remember and access its lexical scope, a closure is generated, even if the function is executed outside the current lexical scope.

Closures are the natural result of writing code based on lexical scopes, and you don't even have to consciously create closures to take advantage of them. The creation and use of closures is ubiquitous in your code.

What are closures?

Take a look at the following example 1:

function foo () {    var a = 2;     function Bar () {        //  2    }    Bar ();} Foo ();

Based on the lexical scope of the lookup rule, the function bar () can access variable A in the outer scope (in this case a RHS reference query).

So this is a closed bag? unfortunately not, because the bar function executes at its defined lexical scope.

But a little modification is a closed bag, Example 2:

function foo () {    var a = 2;     function Bar () {        console.log (a);    }     return Bar;} var baz =//  2--friend, this is the effect of closures. 

The Baz function execution actually only invokes the internal function bar () by referencing a different identifier.

The bar () function can obviously be executed normally, outside of its own defined lexical scope .

According to the rules of the scope, the function bar () function can access the internal scope of Foo (), so after the execution of Foo (), its internal scope is not reclaimed, and bar () still holds a reference to that scope, and this reference is called a closure.

This function is called outside of the lexical scope at the time of definition. Closures allow a function to continue to access the lexical scope at the time of definition.

Of course, regardless of how the value of a function type is passed, closures can be observed when the function is called elsewhere.

See Example 3:

function foo () {    var a = 2;     function Baz () {        //  2    }    Bar (baz);} function Bar (FN) {    //  mom Look, this is the closure! }

Yes, this is also a closure, where Baz is passed out and executed in the bar () function, not in its own defined lexical scope, but it retains this reference to the lexical scope of the definition

See Example 4 again:

var fn; function foo () {    var a = 2;     function Baz () {        console.log (a);    }     // assigning a Baz to a global variable }function  Bar () {    ///  mom Look, this is the closure! // 2  

Yes, this is still a closure . Regardless of the means by which an intrinsic function is passed to the lexical scope where it resides, it holds a reference to the original definition scope, and the closure is used wherever the function is executed.

Let's look at a harder example 5:

functionfunction("Hello, closure!");

Is this a closed bag? The answer is yes, here we pass the timer () function to settimeout, and the timer function can access the internal scope of wait , maintaining a reference to the wait internal scope, such as the message variable inside.

At this time you must be wondering: wrong? Where is this being done? Does it mean that you want to execute outside of the defined lexical scope?

the incoming timer function will of course be executed, only the internal engine call executes .

Deep into the engine's internal principle, the built-in tool function settimeout (..) holds a reference to a parameter, which may be called FN or func, or some other similar name. The engine calls this function, which in the example is the internal timer function, while the lexical scope remains intact throughout the process, and the time function maintains a reference to the wait internal scope.

Iife (execute function immediately) is it a closed packet?

Example 6:

var a = 2;(function  iife () {console.log (a);}) ();

According to our definition, this is not a closure.

However, although Iife itself is not an appropriate example of observing closures, it does create closures and is the most commonly used tool for creating closures that can be closed.

So Iife is really tied to closures, even if they don't actually use closures.

This is also why it is difficult to define the closure, because if it is from memory or function, Iife creates a closure (that is, an area is created in memory, which holds a reference to the scope of the scope chain, which is later shown in Example 9), or the effect is equivalent to creating a closure.

And if you look at the definition of closure, it's not a closed packet.

Let's look at example 7:

 for  (var i=1; i<=5; i++function  timer () {console.log (i); }, I*1000 );}

We all know that this code will output five times 6, why?

Because SetTimeout () is an asynchronous function that executes the callback function in SetTimeout () after the end of the loop, there is no block-level scope in the For loop, that is, the I declaration is in the global scope, and there is only one I ( Because var declares a variable declaration promotion, which is actually declared only once, the value of this I is 6 after the for loop ends. The callback function in the SetTimeOut () timer () keeps a reference to I, but 5 times the timer () function refers to just the same I, so output 5 times 6.

Example 8:

 for (var i=1; i<=5; i++) {    (function() {        function  timer () {            Console.log (i);        }, I*1000 );    }) ();}

Is that effective? The answer is no, although by iife each time a scope is created, but this scope is empty (that is, create an empty scope), so also along the lexical scope chain to the previous layer to find I, the result is found in the global scope of I, that is, only one I, or output five times 6.

So we need to change this to see Example 9:

//it needs to have its own variable to store the value of I in each iteration: for(varI=1; i<=5; i++) {    (function() {        varj =i; SetTimeout (functiontimer () {console.log (j); }, J*1000 ); })();}//All right! It will work!. //you can make some improvements to this code: for(varI=1; i<=5; i++) {    (function(j) {SetTimeout (functiontimer () {console.log (j); }, J*1000 ); }) (i);}//of course, you can write that too . for(varI=1; i<=5; i++) {    (function(i) {setTimeout (functiontimer () {console.log (i); }, I*1000 ); }) (i);}

Using Iife within an iteration will generate a new scope for each iteration, allowing the callback of the deferred function to enclose the new scope within each iteration, and each iteration will contain a variable with the correct value for us to access.

Fortunately, ES6 came out. Let is not a variable declaration promotion, and has the effect of block-level scope, that is, this will produce 5 I memory space, is referenced by five timer () function.

Example 10:

 for  (Let I=1; i<=5; i++function  timer () {console.log (i); }, I*1000 );}

About the garbage collection of closures

1: Does the closure cause a memory leak?

We often say that closures can cause memory leaks, is that true? The answer is no.

The reason for this is that closures can cause garbage leaks because the previous versions of IE9 use different garbage collection methods for JavaScript objects (markup cleanup) and COM objects (reference counts). Therefore closures in these versions of IE can cause some special problems. Specifically, if an HTML element is stored in the scope chain of the closure, it means that the element will not be destroyed

Example 11:

function Assignhandler () {    var element = document.getElementById ("someelement");     function () {        alert (element.id);    };}

The code above creates a closure that acts as an element event handler, and the closure creates a circular reference. Because the anonymous function holds a reference to the active object of Assignhandler (), it causes the reference count of element to not be reduced. As long as an anonymous function exists, the reference number of element is at least 1, so the memory it consumes will never be recycled.

The solution is to save a copy of the element.id in a variable, eliminating the loop reference to the variable in the closure and setting the element variable to null.

Example 12:

function Assignhandler () {    var element = document.getElementById ("someelement");     var id = element.id;     function () {        alert (ID);    };     NULL ;}

Question 2: Are variables that are not used in closures recycled?

The answer is yes.

See Example 13:

function foo () {    var x = {};     var y = "whatever";     return function Bar () {        alert (y);    };} var z = foo ();

where x is not used, will x be recycled? The answer is yes.

Theoretically, the bar function holds references in the Foo scope, so x should not be recycled. But the modern JavaScript engine is very intelligent and is optimized here.

After the JavaScript engine has escaped analysis (parsing the function call relationship to determine if the variable "escapes" the current scope), it is determined that X is not used in the closure, then it removes the X from the scope in the heap.

How is it usually analyzed? It is simple, if there is no reference to this variable in the closure and is not used eval ornew Function,那么javascript引擎可以知道闭包的内存中的作用域不需要这个变量x.

Specific tests can be seen before Masaki an article: JS closure test

Or you can look at an answer on StackOverflow: JavaScript Closures concerning unreferenced Variables

Question 3: Is the variable in the function in the closure allocated in the heap or in the stack?

In a simple interpreter implementation, the variables in the function are allocated on the heap rather than on the stack. The modern JS engine is of course quite cool, through the escape analysis can know what can be allocated on the stack, which needs to be allocated on the heap.

That is, the variables used in closures are allocated in the heap and are not used to be allocated on the stack (for simple types) to facilitate recycling.

Example 13, x, is not used by a closure, but a complex type, so it is in memory is a variable x stored in the stack, and the value of x in the stack is the address of the object {} in the heap, presumably the following

"Stack x"----> (Heap {})

In example 13, Y, which is used by the closure, is based on the original lexical scope, which allocates memory separately in the heap, that is, the closed packet is saved in the heap, and the variables used are stored in the heap with the closure, presumably below.

(Heap (Closure (y: "Whatever")))

Well, this is my personal understanding, if there are any questions or suggestions to welcome the discussion.

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.