Learn JavaScript's closure _javascript skills with me

Source: Internet
Author: User
Tags anonymous closure garbage collection instance method

What exactly is a JavaScript closure?

With JavaScript for more than a year, closures always make a person two Zhang Monk touch the head. Land continued to come into contact with some of the closures of knowledge, have also made several times because do not understand the closure caused by the error, more than a year of data also read some, but still not very clear, recently happened to read the jquery Basic tutorial appendix, found in Appendix A to the JavaScript closure of the description is simple and easy to understand, So Jiehuaxianfo summed up.

1. Definition

Closures: A function that has access to a variable in another function scope. A common way to create closures is to create another function inside a function.

Directly on the 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 A;
2), function a returns function B.

So after the Var c=a () is executed, the variable C actually points to function B, then executes C () and pops up a window showing the value of I (first time 1). This piece of code actually creates a closure, why? Because the variable C outside function A refers to function B within function A, that is to say:
A closure is created when the internal function B of function A is referenced by a variable outside function a.

I guess you still don't understand closures, because you don't know what the closure does, so let's go ahead and explore.

2. What is the function of closure?

In short, the function of a closure is that after a is executed and returned, the closure makes the JavaScript garbage collection mechanism GC not reclaim the resource occupied by a, because the execution of the internal function B of a relies on the variables in a. This is a very straightforward description of the closure function, unprofessional and not rigorous, but the general meaning is that, understand the closure requires a step-by-step process.
In the above example, because the closure makes function a return, I always exist in a, so each C (), I is the value of alert out I after 1.

So let's imagine the other case, if a returns a function B, the situation is completely different. Since A is executed, B is not returned to the outside of a, but is referenced by a, while a is only referenced by B, so functions A and B refer to 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 for JavaScript is described later in detail)

3. The microcosm within the closure package

To get a deeper understanding of the relationship between closures and function A and nested function B, we need to introduce several other concepts: the execution Environment for functions (excution context), active objects (call object), scope (scope), scope chain. Use function A as an example to illustrate these concepts from the process of definition to execution.

1, when defining function A, the JS interpreter sets the scope chain (scope chain) of function A to the "environment" where a is defined, and if a is a global function, only the window object in scope chain.
2, when function a executes, a will enter the corresponding execution environment (excution context).
3 in the process of creating an execution environment, first adds a scope property to a, the scope of a, whose value is the scope chain in step 1th. That is, the scope chain of the a.scope=a.
4), and then the execution environment creates an active object (call object). The active object is also an object that has attributes, but it does not have a prototype and is not directly accessible through JavaScript code. After the active object is created, the active object is added to the top of the scope chain of a. At this point A's scope chain contains two objects: A's active object and a Window object.
5, the next step is to add a arguments property on the active object that holds the arguments passed when the function A is invoked.
6), and finally the reference to all function A's parameters and internal function B is also added to the active object of a. In this step, the definition of function B is complete, so as in step 3rd, the scope chain of function B is set to the environment defined by B, that is, the scope of a.

In this, the entire function A is completed from the definition to the execution step. At this point a returns the reference of function B to C, and the scope chain of function B contains a reference to the active object of function A, which means that B can access all the variables and functions defined in a. Function B is referenced by C, function B is dependent on function A, so function A is not reclaimed by GC after it returns.

When function B is executed it will be similar to the above steps. As a result, the scope chain of B on Execution contains 3 objects: B's Active object, A's active object, and Window object, when a variable is accessed in function B, the search order is to search for its own active object first, and if it does, it returns if there is no active object that will continue searching for function A, and then search, Until we find it. Returns undefined if the entire scope chain is not found. If a prototype prototype object exists in function B, it looks for its own prototype object after searching for its own active object, and then continues to find it. This is the variable lookup mechanism in JavaScript.

4, closure of the application of the scene

1), the protection function of the variable security. Take the first example, in function A, I have only function B to access, and I cannot access it through other means, thus protecting the security of I.
2), in memory to maintain a variable. As in the previous example, because of the closure, function A has always been in memory, so every time C () is executed, I will add 1 to my self.
The above two points are the most basic application scenarios for closures, and many classic cases derive from this.

5, JavaScript garbage collection mechanism

In JavaScript, if an object is no longer referenced, the object is reclaimed by GC. If two objects are referenced to each other and are no longer referenced by the 3rd, then the two referenced objects are also reclaimed. Because function A is referenced by B, B is referenced by C outside of a, which is why function A is not recycled after it executes.

There is no block-level scope in JavaScript, so we use closures in order to declare some local variables that only the function can use, so that we can greatly reduce the variables in the global scope and purify the global scope.

The advantage of using closures is, of course, that the benefits are paid for, at the expense of memory.

How do you understand the sentence above?

The execution of each function creates a function execution environment that is related to the function, or a function execution context. In this execution context, there is a property scope chain (scope chain pointer), which points to a scope chain structure, and the pointer in the scope chain points to the active object corresponding to each scope. Normally, a function creates the function execution context and the corresponding scope chain at the start of the call, releasing the function execution context and the space occupied by the corresponding scope chain after the function execution ends.

declaring function
Test () {
 var str = "Hello world";
 Console.log (str);
Call function
test ();

The structure of the following diagram is generated in memory when the function is invoked:

But the closure is a bit special, because the closure function can access the variables in the outer function, so when the outer function is finished, its scope active object is not freed (note that the execution environment and the corresponding scope chain will be destroyed after the outer function execution ends), but it is referenced by the scope chain of the closure function. The scope active object of the outer function will not be destroyed until the closure function is destroyed. This is why closures are taking up memory.

Therefore, the use of closures has both advantages and disadvantages, and misuse of closures can result in significant memory consumption.

There are other side effects of using closures, which can be said to be bugs or not, and different businesses may have differing views.

This side effect is that the closure function can only be taken to the final value of the outer function variable.

The test code is as follows: (the 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);
 Executes the function $ in the array
 . Res1[0] ();

The above code first opens up a private scope with an anonymous function expression, this anonymous function is the outer function we mentioned above, which has an argument of $, and also defines the variable result and I, assigns an anonymous function to the array by a For loop, and this anonymous function is the closure , he visited the outer function of the variable I, theoretically the array resulti will return the corresponding array subscript value, the actual situation is not as desired.

As above the code $. The result of the RES10 is 10.

Why is this, because the final value of I is 10.

Let's go through the figure below to specify exactly what happens in memory when the above code executes:

So is there any way to fix this side effect? Of course!

We can use the following code to achieve our expectations.

 /* Fix the closure defect * *
 (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 memory? We also use the following picture to explain in detail. It is not difficult for us to understand the figure below when we understand the above picture.

6. A simple example

First from a classic error, there are several div on the page, we want to bind them to a onclick method, so we 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 happens to be wrong, each 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].onc lick = function () {
    alert (num);
   }
  }) (i);
 }
});

7. Internal function

Let's start with some basic knowledge about the internal functions. An intrinsic function is a function defined in another function. For example:

function Outerfn () {
 functioninnerfn ()}
}

INNERFN is an internal function that is wrapped in a OUTERFN scope. This means that calling INNERFN inside 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 ();//uncaught REFERENCEERROR:INNERFN is not defined

However, if you call INNERFN inside OUTERFN, you can run successfully:

function Outerfn () {
   document.write ("Outer function<br/>");
   function Innerfn () {
    document.write ("Inner function<br/>");
   }
   INNERFN ();
  }
  OUTERFN ();

8, the Great Escape (internal function how to escape the external function)

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:

Define global variables to escape
var Globalvar;
function Outerfn () {
 document.write ("Outer function<br/>");   
 function Innerfn () {
  document.write ("Inner function<br/>");
 }
 Globalvar = INNERFN;
}
OUTERFN (); Outer function Inner function
globalvar (),//outer function Inner function Innerfn
();//referenceerror: INNERFN is not defined

When OUTERFN is invoked, the global variable Globalvar is modified, when its reference becomes INNERFN, and then calls Globalvar and calls INNERFN. Calling INNERFN directly outside the OUTERFN will still result in an error, because the intrinsic function, although it escaped by saving the reference in a global variable, is still the name of the function in the scope of the OUTERFN.

You can also get an internal 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 the global variable inside the OUTERFN, a reference to INNERFN is returned from the OUTERFN. This reference can be obtained by invoking OUTERFN, and the reference may be saved in a variable.

The fact that the internal function can be invoked by reference even if it leaves the function scope means that JavaScript needs to retain the referenced function whenever there is a possibility of invoking an intrinsic function. and the JavaScript runtime needs to track all the variables that refer to the internal function until the last variable is discarded, and the JavaScript garbage collector frees up the appropriate memory space (the red part is the key to understanding the closure).

After half a day, I finally had a relationship with the closure, a closure is a function that has permission to access a variable of another function, and the common way to create a closure is to create another function inside a function, which is the internal function we're talking about, so it's not nonsense, it's a closure-related ^_^

9, the scope of the variable

Internal functions can also have their own variables, which are limited to the scope of the internal 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 otherwise, a new Innervar variable is created, followed by 1, and the final display

Outer function
Inner function Innervar = 1
Inner function Innervar = 1
Outer function
Inner function Inner Var = 1
Inner function Innervar = 1

Internal 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 continuously incremented each time the internal function is invoked:

Outer function
Inner function Globalvar = 1
Inner function Globalvar = 2
Outer function
Inner functio n Globalvar = 3
Inner function Globalvar = 4

But what if the 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), internal 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 result is very interesting, perhaps or to our surprise

Outer function
Inner function Outervar = 1
Inner function Outervar = 2
Outer function
Inner function o Utervar = 1
Inner function Outervar = 2

What we see is the effect of the previous two cases, the INNERFN will be incremented independently by each reference invocation Outervar. This means that the second call OUTERFN does not continue to follow the Outervar value, but instead creates and binds a new Outervar instance in the scope of the second function call, with two counters completely unrelated.

When an intrinsic function is referenced outside the scope that defines it, a closure of the intrinsic function is created. In this case, we say that neither the internal function local variable nor its parameter is a free variable, and that the calling environment of the external function is a closed-closure environment. Essentially, if an intrinsic function references a variable in an external function, it is tantamount to authorizing the variable to be deferred. Therefore, when external function calls are complete, the memory of these variables is not freed (the last value is saved), and closures still need to be used.

10. The interaction between closures

When multiple internal functions are present, unexpected closures are likely to occur. We define an increment function, the increment of this function 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 internal functions that can be invoked by the returned reference, resulting in the following:

Outer function
Inner function 1 outervar = 1
Inner function 2 Outervar = 3
Inner function 1 Outervar = 4
O uter function
Inner function 1 outervar = 1
Inner function 2 Outervar = 3
Inner function 1 Outervar = 4

InnerFn1 and InnerFn2 refer to the same local variable, so they share a closed environment. When innerFn1 for Outervar increments for a while, long absence innerFn2 set a new threshold for Outervar, and vice versa. We also see that subsequent calls to OUTERFN also create new instances of these closures. It also creates a new enclosing environment, essentially creating a new object in which the free variable is the instance variable of the object, and the closure is the instance method of the object, and the variables are private, Because these variables cannot be directly referenced outside the scope that encapsulates them, this ensures the exclusivity of object-oriented data.

11. FAQ
Now we can look back at the example at the beginning and it's easy to see why the first way of writing is alert 4 at a time.

for (var i = 0; i < spans.length i++) {
 Spans[i].onclick = function () {
  alert (i);
 }
}

The above code executes when the page is loaded, and when I's value is 4, the For loop execution is complete, but because each span's OnClick method is an intrinsic function at this time, I am a closed-pack reference (a reference to the closure reference), the memory cannot be destroyed, and the value of I is maintained at 4. It will not be reclaimed until the program changes it or if all the onclick functions are destroyed (by actively assigning the function to null or the page is unloaded). So every time we click on the span, the onclick function looks at the value of I (the scope chain is the reference), 4, and then alert 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 the expression, followed by parentheses and brackets is called, and then the I when the argument passed, the function immediately execute, NUM save each time I value.

This pass down presumably everyone also like me, on the closure of some understanding of it, of course, fully understand the words need to the function of the implementation of the environment and scope of the chain to find out.

Related Article

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.