closure

Understand JavaScript Closures With Ease

 

Closures allow JavaScript programmers to write better code. Creative, expressive, and concise. We frequently use closures in JavaScript, and, no matter your JavaScript experience, you will undoubtedly encounter them time and again. Sure, closures might appear complex and beyond your scope, but after you read this article, closures will be much more easily understood and thus more appealing for your everyday JavaScript programming tasks.

This is a relatively short (and sweet) post on the details of closures in JavaScript. You should be familiar with JavaScript variable scope before you read further, because to understand closures you must understand JavaScript’s variable scope.

 

What is a closure?
A closure is an inner function that has access to the outer (enclosing) function’s variables—scope chain. The closure has three scope chains: it has access to its own scope (variables defined between its curly brackets), it has access to the outer function’s variables, and it has access to the global variables.

The inner function has access not only to the outer function’s variables, but also to the outer function’s parameters. Note that the inner function cannot call the outer function’s argumentsobject, however, even though it can call the outer function’s parameters directly.

You create a closure by adding a function inside another function.
A Basic Example of Closures in JavaScript:


 1      function showName (firstName, lastName) {

 2var nameIntro = "Your name is ";
 3      // this inner function has access to the outer function's variables, including the parameter​
 4function makeFullName () {

 5return nameIntro + firstName + " " + lastName;

 6      }
 7  8return makeFullName ();

 9      }

10 11      showName ("Michael", "Jackson"); // Your name is Michael Jackson

 this is the explain of the above code:

The celebrityName function returns a function; the function it returns is the lastName inner function.
So when we do:
var mjName = celebrityName (“Michael”);
The mjName variable now contains the lastName inner function that was returned when we called celebrityName (). And the inner function has all the variables, including the name “Michael” that was passed into the celebrityName function.

To prove that the mjName variable contains the lasName function, we can do this:

 

1      console.log (mjName);
2 3// The output is this:​
4function lastName(theLastName) {
5      return nameIntro + firstName + " " + theLastName;
6      }
7

 

So mjName is a function expression: it is a variable with a value of a function.
Now, all we have to do to call the function stored in mjName is add the parentheses () to execute the function, like this:
mjName (); //When it executes, the following line from the lastName inner function runs;
return nameIntro + firstName + ” ” + theLastName;
And this is why the output is: “This celebrity is Michael Jackson”
When we do mjName (“Jackson”);

 

 

Closures are used extensively in Node.js; they are workhorses in Node.js’ asynchronous, non-blocking architecture. Closures are also frequently used in jQuery and just about every piece of JavaScript code you read.
A Classic jQuery Example of Closures:


 

1      $(function() {
2 3var selections = [];
4      $(".niners").click(function() { // this closure has access to the selections variable​
5      selections.push (this.prop("name")); // update the selections variable in the outer function's scope​
6      });
7 8      });

 

Closures have access to the outer function’s variable even after the outer function returns:Closures’ Rules and Side Effects

    1. One of the most important and ticklish features with closures is that the inner function still has access to the outer function’s variables even after the outer function has returned. Yep, you read that correctly. When functions in JavaScript execute, they use the same scope chain that was in effect when they were created. This means that even after the outer function has returned, the inner function still has access to the outer function’s variables. Therefore, you can call the inner function later in your program. This example demonstrates:

       

       1      function celebrityName (firstName) {
       2      var nameIntro = "This celebrity is ";
       3      // this inner function has access to the outer function's variables, including the parameter​
       4      function lastName (theLastName) {
       5      return nameIntro + firstName + " " + theLastName;
       6      }
       7      return lastName;
       8      }
       9 10var mjName = celebrityName ("Michael"); // At this juncture, the celebrityName outer function has returned.​
      11 12// The closure (lastName) is called here after the outer function has returned above​
      13// Yet, the closure still has access to the outer function's variables and parameter​
      14      mjName ("Jackson"); // This celebrity is Michael Jackson

       


    2. Closures store references to the outer function’s variables; they do not store the actual value. 
Closures get more interesting when the value of the outer function’s variable changes before the closure is called. And this powerful feature can be harnessed in creative ways, such as this private variables example first demonstrated by Douglas Crockford:

       1      function celebrityID () {
       2      var celebrityID = 999;
       3      // We are returning an object with some inner functions​
       4      // All the inner functions have access to the outer function's variables​
       5      return {
       6      getID: function () {
       7      // This inner function will return the UPDATED celebrityID variable​
       8      // It will return the current value of celebrityID, even after the changeTheID function changes it​
       9      return celebrityID;
      10      },
      11      setID: function (theNewID) {
      12      // This inner function will change the outer function's variable anytime​
      13      celebrityID = theNewID;
      14      }
      15      }
      16 17      }
      18 19var mjID = celebrityID (); // At this juncture, the celebrityID outer function has returned.​
      20      mjID.getID(); // 999​
      21      mjID.setID(567); // Changes the outer function's variable​
      22      mjID.getID(); // 567: It returns the updated celebrityId variable

       

    3. Closures Gone Awry
      
Because closures have access to the updated values of the outer function’s variables, they can also lead to bugs when the outer function’s variable changes with a for loop. Thus:

       

       1      // This example is explained in detail below (just after this code box).​
       2function celebrityIDCreator (theCelebrities) {
       3      var i;
       4      var uniqueID = 100;
       5      for (i = 0; i < theCelebrities.length; i++) {
       6      theCelebrities[i]["id"] = function () {
       7      return uniqueID + i;
       8      }
       9      }
      10       
      11      return theCelebrities;
      12      }
      13 14var actionCelebs = [{name:"Stallone", id:0}, {name:"Cruise", id:0}, {name:"Willis", id:0}];
      15 16var createIdForActionCelebs = celebrityIDCreator (actionCelebs);
      17 18var stalloneID = createIdForActionCelebs [0];

console.log(stalloneID.id()); // 103

       

      In the preceding example, by the time the anonymous functions are called, the value of i is 3 (the length of the array and then it increments). The number 3 was added to the uniqueID to create 103 for ALL the celebritiesID. So every position in the returned array get id = 103, instead of the intended 100, 101, 102.

      The reason this happened was because, as we have discussed in the previous example, the closure (the anonymous function in this example) has access to the outer function’s variables by reference, not by value. So just as the previous example showed that we can access the updated variable with the closure, this example similarly accessed the i variable when it was changed, since the outer function runs the entire for loop and returns the last value of i, which is 103.

      To fix this side effect (bug) in closures, you can use an Immediately Invoked Function Expression (IIFE), such as the following:

       1      function celebrityIDCreator (theCelebrities) {
       2      var i;
       3      var uniqueID = 100;
       4      for (i = 0; i < theCelebrities.length; i++) {
       5      theCelebrities[i]["id"] = function (j) { // the j parametric variable is the i passed in on invocation of this IIFE​
       6      return function () {
       7      return uniqueID + j; // each iteration of the for loop passes the current value of i into this IIFE and it saves the correct value to the array​
       8      } () // BY adding () at the end of this function, we are executing it immediately and returning just the value of uniqueID + j, instead of returning a function.​
       9      } (i); // immediately invoke the function passing the i variable as a parameter​
      10      }
      11 12      return theCelebrities;
      13      }
      14 15var actionCelebs = [{name:"Stallone", id:0}, {name:"Cruise", id:0}, {name:"Willis", id:0}];
      16 17var createIdForActionCelebs = celebrityIDCreator (actionCelebs);
      18 19var stalloneID = createIdForActionCelebs [0];
      20      
console.log(stalloneID.id); // 100​
      21 22var cruiseID = createIdForActionCelebs [1];
console.log(cruiseID.id); // 101

      原文链接:http://javascriptissexy.com/understand-javascript-closures-with-ease/


posted @ 2016-04-22 15:58  RachelChen  阅读(444)  评论(0编辑  收藏  举报