Modularization of JavaScript: encapsulation (closure), inheritance (prototype) Introduction

Source: Internet
Author: User

Although JavaScript is naturally just a casual look, as more and more things can be done by browsers, this language is also increasingly exposed. In complex logic, JavaScript needs to be modularized, and modules need to be encapsulated, leaving only interfaces for external calls. Closures are the key to implementing module encapsulation in JavaScript, and are also difficult for beginners to understand. At first, I was confused. Now, I am confident that I have a deep understanding of this concept. To facilitate understanding, we try to encapsulate a simple object.

We try to maintain a counter object ticker on the page, which maintains a value of n. With the user's operation, we can increase the count once (add the value n to 1), but cannot decrease n or directly change n. In addition, we need to query this value from time to time.

Modular JSON-style Portal

One way to expand a portal is:
Copy codeThe Code is as follows:
Var ticker = {
N: 0,
Tick: function (){
This. n ++;
},
};

This method is natural and effective. When we need to add a count, we call the ticker. tick () method and access the ticker. n variable when we need to query the number of times. However, its disadvantage is obvious: the module user is allowed to change n freely, such as calling ticker. n -- or ticker. n =-1. We didn't encapsulate ticker. n and tick () seem to be "members" of ticker, but their accessibility is the same as that of ticker, all are global (if ticker is a global variable ). In terms of encapsulation, this modular approach is a little more ridiculous than the following (although this is enough for some simple applications ).

Copy codeThe Code is as follows:
Var ticker = {};
Var tickerN = 0;
Var tickerTick = function (){
TickerN ++;
}

TickerTick ();

It is worth noting that in tick (), I access this. n -- not because n is a member of ticker, but because ticker () is called. In fact, it would be better to write ticker. n here, because if tick () is called, it is not a ticker, but something else, such:

Copy codeThe Code is as follows:
Var func = ticker. tick;
Func ();

At this time, the call of tick () is actually a window, and the function execution will attempt to access window. n and an error occurs.

In fact, this "portal-wide open" modular approach is often used to organize JSON-style data rather than programs. For example, we can pass the following JSON object to a function of ticker to determine that ticker starts counting from 100 and increments to 2 each time.

Copy codeThe Code is as follows:
Var config = {
Nsstart: 100,
Step: 2
}

Scope chain and Closure
Let's take a look at the following code. Note that we have passed in config to customize ticker.

Copy codeThe Code is as follows:
Function ticker (config ){
Var n = config. nStart;
Function tick (){
N + = config. step;
}
}
Console. log (ticker. n); //-> undefined

You may wonder why ticker has changed from an object to a function? This is because in JavaScript, only the function has a scope and the internal variables of the function cannot be accessed from the function body. Access ticker. n outside of ticker () to get undefined, but access n in tick () is normal. From tick () to ticker () to global, this is the "Scope chain" in JavaScript ".

But there is still a problem, that is-how to call tick ()? The scope of ticker () also masks tick. There are two solutions:

• 1) call a method as the return value, just as we use the method of increasing n as the return value of ticker;
• 2) variables that set the outer scope, as we set getN in ticker.

Copy codeThe Code is as follows:
Var getN;
Function ticker (config ){
Var n = config. nStart;
GetN = function (){
Return n;
};
Return function (){
N + = config. step;
};
}

Var tick = ticker ({nStart: 100, step: 2 });
Tick ();
Console. log (getN (); //-> 102

The Variable n is in the "closure", and cannot be directly accessed outside of ticker (), but it can be observed or manipulated in two ways.

In the first code in this section, after the ticker () method is executed, n and tick () are destroyed until the next call to this function is created; however, in the second code, after ticker () is executed, n will not be destroyed, because tick () and getN () may access or change it, and the browser will be responsible for maintaining n. My understanding of the "closure" is the mechanism to ensure that n is in the function scope, and the variables that may be accessed in other ways will not be destroyed after the function is executed.

But I still don't think it's okay? What if I need to maintain two objects with the same function, ticker1 and ticker2? There is only one ticker (). I can't write it again, can I?

New operator and constructor
If a function is called using the new operator, a new object is created and called using this object. In my understanding, the construction process of t1 and t2 in the following code is the same.

Copy codeThe Code is as follows:
Function myClass (){}
Var t1 = new myClass ();
Var t2 = {};
T2.func = myClass;
T2.func ();
T2.func = undefined;

T1 and t2 are newly constructed objects, and myClass () is the constructor. Similarly, ticker () can be rewritten.

Copy codeThe Code is as follows:
Function TICKER (config ){
Var n = config. nStart;
This. getN = function (){
Return n;
};
This. tick = function (){
N + = config. step;
}
}

Var ticker1 = new TICKER ({nStart: 100, step: 2 });
Ticker1.tick ();
Console. log (ticker1.getN (); //-> 102
Var ticker2 = new TICKER ({nStart: 20, step: 3 });
Ticker2.tick ();
Ticker2.tick ();
Console. log (ticker2.getN (); //-> 26

Traditionally, constructors are capitalized. Note that TICKER () is still a function, not a pure object ("pure", because the function is actually an object, and TICKER () is a function object). The closure is still valid, we cannot access ticker1.n.

Prototype and inheritance
The above TICKER () still has a defect, that is, ticker1.tick () and ticker2.tick () are independent of each other! Please note that every time TICKER () is called using the new operator, a new object is generated and a new function is generated and bound to this new object. Each time a new object is constructed, the browser needs to open up a space to store the variables in tick () itself and tick (). This is not what we expected. We expect ticker1.tick and ticker2.tick to point to the same function object.

This requires the introduction of a prototype.

In JavaScript, all objects except the Object have a prototype attribute, which points to another Object. This "another Object" still has its prototype Object, forming a prototype chain, and finally pointing to the Object. When a method is called on an Object, if you find that this Object has no specified method, you can find this method on the prototype chain until the Object.

Functions are also objects, so functions also have original objects. When a function is declared (that is, when the function Object is defined), a new Object is generated. The prototype attribute of this Object points to the Object, in addition, the constructor attribute of this object points to the function object.

The new object constructed by the constructor points to the prototype object of the constructor. Therefore, we can add functions on the prototype object of the constructor. These functions depend not on ticker1 or ticker2, but on TICKER.

You may do this:

Copy codeThe Code is as follows:
Function TICKER (config ){
Var n = config. nStart;
}
TICKER. prototype. getN = function {
// Attention: invalid implementation
Return n;
};
TICKER. prototype. tick = function {
// Attention: invalid implementation
N + = config. step;
};

Note that this is an invalid implementation. Because the method of the prototype object cannot access the content in the closure, that is, variable n. After the TICK () method is run, n cannot be accessed again. the browser will destroy n. To access the content in the closure, the object must have some simple instance-dependent methods to access the content in the closure, and then define complex public methods on its prototype to implement logic. In fact, the tick () method in this example is concise enough. Let's put it back in TICKER. The following describes how to implement tickTimes (), which allows the caller to specify the number of times tick () is called.

Copy codeThe Code is as follows:
Function TICKER (config ){
Var n = config. nStart;
This. getN = function (){
Return n;
};
This. tick = function (){
N + = config. step;
};
}
TICKER. prototype. tickTimes = function (n ){
While (n> 0 ){
This. tick ();
N --;
}
};
Var ticker1 = new TICKER ({nStart: 100, step: 2 });
Ticker1.tick ();
Console. log (ticker1.getN (); //-> 102
Var ticker2 = new TICKER ({nStart: 20, step: 3 });
Ticker2.tickTimes (2 );
Console. log (ticker2.getN (); //-> 26

This TICKER is good. It encapsulates n and cannot be directly changed from outside the object. The complex function tickTimes () is defined on the prototype. This function operates the data in the object by calling the small function of the instance.

Therefore, in order to maintain object encapsulation, I suggest decoupling operations on data into Unit functions as small as possible, the constructor is defined as dependent on instances (in many places it is also called "private"), and implements complex logic on the prototype (that is, "public ).

Finally, let's talk about inheritance. In fact, when we define a function on the prototype, we have used inheritance! Inheritance in JavaScript is more... than in C ++ ...... Er ...... Simple or simple. In C ++, we may define an animal class to represent animals, and then define the bird class to inherit the animal class to represent birds, but what I want to discuss is not this inheritance (although this inheritance can also be implemented in JavaScript); what I want to discuss is that in C ++, an animal class is defined, then a myAnimal object is instantiated. Yes, this is instantiated in C ++, but it is treated as inheritance in JavaScript.

JavaScript does not support classes. browsers only care about the objects they currently have, without worrying about the structure of the classes they belong. In our example, TICKER () is a function object. We can assign a value to it (TICKER = 1) and delete it (TICKER = undefined ), however, because ticker1 and ticker2 are called through the new operator, TICKER () acts as a constructor, while TICKER. prototype Objects Act as classes.

The above is the modular JavaScript method I know. If you are a beginner, I hope it will help you. If something is wrong, you can also point it out.

Author: yiye Jaipur
Source: www.cnblogs.com/yiyezhai

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.