JavaScript closures for what scenario __java

Source: Internet
Author: User
Tags closure function definition getmessage mozilla developer network

This article translates from MDN (Mozilla Developer Network): Original address: MDN translation address: Shixinzhang Blog

After reading this article you will learn:

The callback of the actual scene of the closure of lexical scope closed package simulation Private method common errors creating closures in loops pay attention to performance

Lexical scopes

Consider the following code:

function init () {
  var name = ' Mozilla ';//name is the local variable function created by the INIT function
  displayName () {//DisplayName () is the internal method of the functions , a closure
    alert (name);//It uses the variable declared by the parent function   
  }
  displayName ();    
}
Init ();

The init () function creates the local variable name and function DisplayName ().

DisplayName () is an internal function defined within init () and is therefore accessible only within the init () function. DisplayName () has no internal variables, but because internal functions can access variables of external functions, DisplayName () can access the variable name in init ().

Running the above code, we can see that the value of name is successfully printed.

This is an example of the lexical scope, which describes how the JS parser handles variables in nested functions .

A lexical scope refers to the position in which a variable is declared in the source code as its scope. Nested functions can access variables declared in their outer scope. Closed Bag

Now look at the following code:

function Makefunc () {
  var name = ' Mozilla ';
  function DisplayName () {
    alert (name);
  }
  return displayName;
}

var myFunc = Makefunc ();
MyFunc ();

Running the code above will have the same result as the first example. The difference is-and interestingly-the internal function displayName () is returned by the external function as the return value before execution.

At first glance, this code is not intuitive, though it can be executed. In some programming languages, local variables within a function survive only during function execution. Once the Makefunc () function completes, you may feel that the name variable cannot exist. However, from the results of the code's operation, JavaScript is obviously different from the "some programming languages" we mentioned earlier about variables.

The "difference" in the code above is that Makefunc () returns a closure.

Closures consist of functions and its lexical environment. This environment refers to all the variables that it can access when a function is created. In the example above, myFunc refers to a closure that consists of the displayName () function and the "Mozilla" string that exists when the closure is created. Because DisplayName () holds a reference to name, MyFunc holds a reference to DisplayName (), so the name is still in an accessible state when called MyFunc.

Here's a more interesting example:

function Makeadder (x) {return
  function (y) {return
    x + y;}
  ;
}

var add5 = Makeadder (5);
var add10 = Makeadder (a);

Console.log (ADD5 (2));  7
Console.log (ADD10 (2));//12

In the example above, Makeadder () takes a parameter X and returns a function whose argument is Y and the return value is x+y.

In essence, Makeadder () is a function factory-you can create a function that sums a parameter with other values by passing in a parameter to it.

In the example above we used a function factory to create two functions, one will give the parameter 5 and the other 10.

Add5 and ADD10 are closures. They use the same function definition, but the lexical environment is different. In Add5, X is 5;add10 x is 10. closure of the actual scene of the closures

The closure is useful because it associates some data with functions that manipulate it. This is remarkably similar to object-oriented programming. In face-image programming, we can associate some data (the properties of an object) with one or more methods.

Therefore, you can use closures when you want to manipulate an object in only one way.

In web programming, there may be a lot of scenarios where you can use closures. Most front-end JavaScript code is "event-driven": we define behavior and then associate it with a user event (click or button). Our code is usually bound to the event as a callback (the function called when the event is triggered).

For example, we want to add a few buttons for a page to adjust the font size. One method is to specify the font-size of the body element in pixels, and then set the font size of other elements (such as headers) in the page by relative EM units.

Here is the CSS code:

Body {
  Font-family:helvetica, Arial, Sans-serif;
  font-size:12px;
}

h1 {
  font-size:1.5em;
}

h2 {
  font-size:1.2em;
}

We modify the Font Size button to modify the Font-size property of the BODY element, and as we use relative units, the other elements in the page are adjusted accordingly.

HTML Code:

    <p>some paragraph text</p>
    

JavaScript Code:

function Makesizer (size) {return
  function () {
    document.body.style.fontSize = size + ' px ';
  }

var size12 = Makesizer (a);
var size14 = Makesizer ();
var size16 = Makesizer (16);

Size12, Size14, and Size16 can now adjust the body's font to 12, 14, 16 pixels respectively. We can then bind them to the button:

document.getElementById (' size-12 '). onclick = Size12;
document.getElementById (' size-14 '). onclick = Size14;
document.getElementById (' size-16 '). onclick = Size16;

Now click on a few buttons, the entire page font will be adjusted.

simulating private methods with closures

Some programming languages, such as Java, can create private methods (methods that can only be called by other methods in the same class).

JavaScript does not support this approach, but we can use closure simulation implementations. Private methods not only restrict access to the code, but also provide the ability to manage the global namespace, avoiding the non-core approach of confusing the public interface of the code.

The following code shows how to use closures to define public functions that access private and private variables. This approach is also called module mode:

var counter = (function () {
  var privatecounter = 0;
  Function Changeby (val) {
    Privatecounter + = val;
  }
  return {
    increment:function () {
      Changeby (1);
    },
    decrement:function () {
      changeby ( -1);
    },
    value:function () {return
      privatecounter;}}   
) ();

Console.log (Counter.value ()); Logs 0
counter.increment ();
Counter.increment ();
Console.log (Counter.value ()); Logs 2
counter.decrement ();
Console.log (Counter.value ()); Logs 1

In the previous example, each closure had its own lexical environment. But in this case, three methods Counter.value (), Counter.increment () and Counter.decrement () share a lexical environment.

This shared environment is created in an anonymous function that executes immediately once defined. The environment contains two private items: A variable named Privatecounter and a function named Changeby. Neither of them can be accessed directly from outside the anonymous function. Must be accessed by three public functions of the object returned by the anonymous wrapper.

Thanks to the lexical scope of JavaScript, these three functions have access to Privatecounter and Changeby (), allowing three closures to share an environment.

You may notice that we created the privatecounter in the anonymous function in the above code, then executed the function immediately, assigned a value to Privatecounter, and then returned the result to counter.

We can also save this function to another variable in order to create multiple counters.

var makecounter = function () {
  var privatecounter = 0;
  Function Changeby (val) {
    Privatecounter + = val;
  }
  return {
    increment:function () {
      Changeby (1);
    },
    decrement:function () {
      changeby ( -1);
    },
    value:function () {return
      privatecounter;
    }}}  
;

var counter1 = Makecounter ();
var counter2 = Makecounter ();
Alert (Counter1.value ()); /* Alerts 0 *
/counter1.increment ();
Counter1.increment ();
Alert (Counter1.value ()); /* Alerts 2 *
/Counter1.decrement ();
Alert (Counter1.value ()); /* Alerts 1/
alert (Counter2.value ());/* Alerts 0 *

Now two counters counter1, Counter2 hold a different lexical environment, they have their respective privatecounter and values. Calling one of the counters does not affect the value of the other.

This use of closures can provide benefits in many object-oriented programming, such as data hiding and encapsulation. Common error: Creating closures in loops

Before Ecmascrpit 2015, there was no let keyword.

Creating a closure in a loop often makes such a mistake, using the following code as an example:

<p id= "Help" >helpful notes would appear here</p> <p>e-mail: <input type=
"text" id= "email" name= "Email" ></p>
<p>name: <input type= "text" id= "name" name= "name" ></p>
<p> Age: <input type= ' text ' id= ' age ' name= ' age ' ></p>
function ShowHelp (Help) {
  document.getElementById ("Help"). InnerHTML = Help;
}

function Setuphelp () {
  var helpText = [
      {' id ': ' email ', ' help ': ' Your e-mail address '},
      {' id ': ' name ', ' Help ': ' Your full Name '},
      {' id ': ' Age ', ' help ': ' Your Age '}
    ];

  for (var i = 0; i < helptext.length i++) {
    var item = helptext[i];    var declares a variable whose scope is in the function body, not within the block
    document.getElementById (item.id). onfocus = function () {
      showHelp (item.help) ;
    }
  }
}

Setuphelp ();

In the above code, HelpText is an array of three IDs associated with the hint information. In the loop, we iterate through the HelpText array, and the component in the array has the response of the focus event added.

If you run the above code, you will find that no matter which input box you choose, the final message displayed is "Your age."

The reason: We assign to the onfocus event is three closures. These three closures are made up of the environment within functions and SETUPHELP () functions.

Three closures were created in the loop, but they all use the same lexical environment Item,item variable item.help with a value variable.

When a onfocus callback executes, the value of the item.help is determined. At that time the loop was over, three closures Shared item objects already point to the last item in the HelpText list.

One way to solve this problem is to use more closures, such as using a previously mentioned function factory:

function ShowHelp (Help) {
  document.getElementById ("Help"). InnerHTML = Help;
}

function Makehelpcallback (Help) {return
  function () {
    showHelp (Help);
  }

function Setuphelp () {
  var helpText = [
      {' id ': ' email ', ' help ': ' Your e-mail address '},
      {' id ': ' name ', ' Help ' : ' Your full Name '},
      {' id ': ' Age ', ' help ': ' Your age (for you must '}
    ];

  for (var i = 0; i < helptext.length i++) {
    var item = helptext[i];
    document.getElementById (item.id). onfocus = Makehelpcallback (item.help);    This uses a function factory
  }

setuphelp ();

This is the correct result of the operation. Unlike the previous example, three callbacks share a lexical environment in which the Makehelpcallback () function is used to create a new lexical environment for each callback. In these environments, help points to the correct corresponding string in the HelpText array.

Another way to use anonymous functions to solve this problem is this:

function ShowHelp (Help) {
  document.getElementById ("Help"). InnerHTML = Help;
}

function Setuphelp () {
  var helpText = [
      {' id ': ' email ', ' help ': ' Your e-mail address '},
      {' id ': ' name ', ' Help ': ' Your full Name '},
      {' id ': ' Age ', ' help ': ' Your age (for you must '}
    ];

  for (var i = 0; i < helptext.length i++) {
    (function () {
       var item = helptext[i];
       document.getElementById (item.id). onfocus = function () {
         showHelp (item.help);
       }
    }) (); Call the binding function immediately, using the correct value to bind to the event, rather than using the value of the Loop End
  }
}

setuphelp ();

If you do not want to use more closures, you can also use the block-level scope let keyword described in ES2015:

function ShowHelp (Help) {
  document.getElementById ("Help"). InnerHTML = Help;
}

function Setuphelp () {
  var helpText = [
      {' id ': ' email ', ' help ': ' Your e-mail address '},
      {' id ': ' name ', ' Help ': ' Your full Name '},
      {' id ': ' Age ', ' help ': ' Your age (for you must '}
    ];

  for (var i = 0; i < helptext.length. i++) {Let
    item = helptext[i];    Limit scopes document.getElementById (item.id) only within the current block
    . onfocus = function () {
      showHelp (item.help);}
  }}

Setuphelp ();

The code above uses let rather than Var to modify the variable item, so each closure is bound to a variable within the current block. No additional closures are required. pay attention to performance

It is not advisable to create functions in other functions without being required. Because closures have a negative impact on script performance, including processing speed and memory consumption.

For example, when creating a new object or class, the method should usually be associated with the object's prototype rather than being defined in the object's constructor. Because this will cause each constructor to be invoked, the method is assigned once (that is, the method is assigned a value again when each object is created).

As an example:

function MyObject (name, message) {
  this.name = name.tostring ();
  This.message = Message.tostring ();
  This.getname = function () {return
    this.name;
  };

  This.getmessage = function () {return
    this.message;}
  ;
}

The code above does not take advantage of the closure, so we can rewrite it like this:

function MyObject (name, message) {
  this.name = name.tostring ();
  This.message = Message.tostring ();
}
Myobject.prototype = {
  getname:function () {return
    this.name;
  },
  getmessage:function ()
    { return this.message;
  }
;

in general, however, it is not recommended to redefine the prototype.

The following code adds an attribute to an existing prototype:

function MyObject (name, message) {
  this.name = name.tostring ();
  This.message = Message.tostring ();
}
MyObject.prototype.getName = function () {return
  this.name;
};
MyObject.prototype.getMessage = function () {return
  this.message;
};

However, we can also abbreviate the above code like this:

function MyObject (name, message) {
    this.name = name.tostring ();
    This.message = Message.tostring ();
}
(function () {
    this.getname = function () {return
        this.name;
    };
    This.getmessage = function () {return
        this.message;}
    ;}
). Call (Myobject.prototype);

In the previous three examples, the inherited prototypes can be shared for all objects, and you do not have to redefine the method every time you create the object.

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.