In fact, closures are not inscrutable.

Source: Internet
Author: User

From the public, "front-end Daquan"-bole online/Liu Jianchu

Count Events -we'll start with a simple question. If the closure is introduced into the program, it will be easy to solve the problem.

We create a mechanism for counting events that will help us track the execution of the code and even debug some problems.

For example:

Increment ();//number of Events:1

Increment ();//number of Events:2

Increment ();//number of Events:3

As you can see in the above case, we want the code to display a message "number of events:n" Each time we execute the increment () function.

The following is a simple way to implement the function:

var counter=0;

function Increment () {

Counter+=1;

Console.log ("Number of events:" +counter);

}

Multiple Counters -The above code is very simple and clear. However, when we introduce the second counter, we will soon encounter a problem. Of course, we can implement two separate counter mechanisms.

The following code:

var counter1=0;

function IncrementCounter1 () {

Counter1+=1;

Console.log ("Number of events:" +counter1);

}

var counter2=0;

function IncrementCounter2 () {

Counter2+=1;

Console.log ("Number of events:" +counter2);

}

IncrementCounter1 ();//number of Events:1

IncrementCounter2 ();//number of Events:1

IncrementCounter1 ();//number of Events:2

Unnecessary duplication occurs in the above code. Obviously, this workaround does not apply to cases that exceed two or three counters. We need to come up with a better solution.

Introduction of the first closure.

In a situation similar to the above example, we introduce a new counter in some way, which bundles a function that can be incremented and does not have a lot of duplicated code.

Here's an attempt to use closures :

function Createcounter () {

var counter=0;

function Increment () {

Counter+=1;

Console.log ("Number of events:" +counter);

}

return increment;

}

We will create two counters and have them follow two separate events:

var counter1=createcounter ();

var counter2=createcounter ();

Counter1 ();//number of events 1

Counter1 ();//number of Events 2

Counter2 ();//number of events 1

Counter1 ();//number of Events 3

Now it looks a little complicated, it's very simple, we just have to divide the implementation logic into a few easy-to-understand chunks and we know what we've achieved:

First, a local variable named counter is worn.

Then, a local function named increment is created, and he can increase the value of the counter variable. This may be very unfamiliar to you if you have never been in touch with functional programming that handles functions as data.

However, this is very common and requires only a few connections to adapt to this concept.

You should note that the implementation of Createcounter () is almost consistent with our previous counter implementations, except that it is packaged or encapsulated in a function body. Therefore, these constructors are called closures

It's a tricky part to start with:

The last step of Createcounter () returns the local function increment. Note that this does not return the run result of the calling function, but rather the function itself, which means that when we create a new one under this code snippet

Counter, you are actually generating a new function.

Fancynewcounter is a function in this scope

Fancynewcounter is a function of the current scope

var fancynewcounter=createcounter ();

This is where the power of the closure life cycle lies. Each generated function maintains a reference to the counter variable created by Createcounter (). In a sense, the returned function remembers when it was created

The environment.

It is important to note here that the internal variable counter exist independently of each scope! For example, if we create two counters, they will be assigned a new counter variable in the closure body.

Observe the following code:

Each counter will be counted from 1:

var counter1=createcounter ();

Counter1 ()//number of events:1

Counter1 ()//number of Events:2

var counter2=createcounter ();

Counter2 ()//number of events:1

The second counter does not interfere with the value of the first counter:

Counter ();//number of Events:3

 Give our counter a name

The information "Number of events:n" is not a problem, but it would be better if you could describe the type of each count event. As the following example, we add a name to the counter:

var catcounter=createcounter ("Cats");

var dogcounter=createcounter ("Dogs");

Catcounter ();//number of Cats:1

Catcounter ();//number of Cats:2

Dogcounter ();//number of Dogs:1

We only need to pass the parameters for the closure to achieve this goal.

function Createcounter (CounterName) {

var counter=0;

function Increment () {

counter++;

Console.log ("Number of" +countername+ ":" +counter);

}

return increment;

}

Note An interesting behavior for the above Createcounter () function. The return function not only remembers the local variable counter, but also remembers the arguments passed in.

  Improving the public interface

The common interface referred to here is how we use counters. This does not simply mean that the value is incremented when the created technology is called.

var dogcounter=createcounter ("Dogs");

Dogcounter.increment ();//number of Dogs:1

Create one of these implementations:

function Createcounter (CounterName) {

var counter=0;

function Increment () {

counter++;

Console.log ("Number of" +countername+ ":" +counter);

}

Return{increment:increment};

}

In the preceding code snippet, we simply returned an object that contained all the features of the datagram. In a sense, we can define a series of information that a closure can return.

Increase one decrement

Now, we can very simply introduce a decrement (decrement) for our counter.

function Createcounter (CounterName) {

var counter=0;

function Increment () {

counter++;

Console.log ("Number of" +countername+ ":" +counter);

}

function Decrement () {

counter--;

Console.log ("Number of" +countername+ ":" +counter);

}

return{

Increment:increment,

Decrement:decrement

}

}

var dogscounter=createcounter ("Dogs");

Dogscounter.increment ();//number of Dogs:1

Dogscounter.increment ();//number of Dogs:2

Dogscounter.decrement ();//number of Dogs:1

Hide counter behavior

Creating a function that is dedicated to displaying the counter is worth the better, let's call the display function.

function Createcounter (CounterName) {

var counter=0;

function display () {

Console.log ("Number of" +countername+ ":" +counter);

}

function Increment () {

counter++;

Display ();

}

function Decrement () {

counter--;

Display ();

}

return{

Increment:increment,

Decrement:decrement

}

}

var dogscounter=createcounter ("Dogs");

Dogscounter.increment ();//number of Dogs:1

Dogscounter:increment ();//number of Dogs:2

Dogscounter:decrement ();//number of Dogs:1

The increment () and decrement () functions look very similar, but this is quite different. We did not return the count value in the result object, which means that the code will fail to invoke:

var dogscounter=createcounter ("Dogs");

Dogscounter.display ();//error

We make the display () function invisible to the outside, and it is only available within createcounter ().

Abstract data types

As you can see, we are able to introduce abstract data types very simply by closures.

For example, let's implement a stack through closures.

function Createstack () {

var elements=[];

return{

Push:function (EL) {elements.unshift (EL);},

Pop:function () {return elements.shift ();}

}

}

var stack=createstack ();

Stack.push (3);

Stack.push (4);

Stack.pop ();//4

Note: In JavaScript, closures are not the best way to implement stack data types. The implementation of prototype is more memory-friendly (when the current object instance cannot find the corresponding property or method, the prototype property that is referenced by the corresponding instance is searched for the corresponding property or method (if the current prototype property is not found, it is looked up along the current prototype chain), While the properties or methods on the prototype are common, unlike the properties or methods of an instance, the child creates properties or methods independently, thereby saving more memory.

  Closures and object-oriented programming

If you have an object-oriented programming experience, you should notice that the appeal constructor looks very much like classes, objects, instance values, and private/public methods.

Closures are similar to classes, and there are some functions that can manipulate internal data to be linked together. As a result, you can use closures anywhere you like with objects.

  Conclusion

Closures are a great property of programming languages. The closure property is very useful when we want to create a "real" hidden field in JavaScript, or if you need to create a simple constructor. But for the general class, closures may be a bit too heavy.

In fact, closures are not inscrutable.

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.