Home  >  Article  >  Web Front-end  >  Modularization of JavaScript: Encapsulation (closure), inheritance (prototype) Introduction_javascript skills

Modularization of JavaScript: Encapsulation (closure), inheritance (prototype) Introduction_javascript skills

WBOY
WBOYOriginal
2016-05-16 17:28:171051browse

Although JavaScript is inherently casual, as browsers are able to do more and more things, the language is becoming more and more serious. Under complex logic, JavaScript needs to be modularized, and the modules need to be encapsulated, leaving only interfaces for external calls. Closure is the key to module encapsulation in JavaScript, and it is also a point that many beginners find difficult to understand. At first, I was confused. Now, I am confident that I have a deeper understanding of this concept. In order to facilitate understanding, the article attempts to encapsulate a relatively simple object.

We are trying to maintain a counter object ticker on the page, which maintains a value n. As the user operates, we can increment the count (add 1 to the value n), but cannot decrement n or change n directly. Moreover, we need to query this value from time to time.

Open-door JSON style modularity

A way to open the door is:

Copy the code The code is as follows:

var ticker = {
n:0,
tick:function(){
this.n ;
},
};

This way of writing is natural and effective. When we need to increase the count, we call the ticker.tick() method. When we need to query the count, we access the ticker.n variable. But its shortcomings are also obvious: users of the module are allowed to change n freely, such as calling ticker.n-- or ticker.n=-1. We have not encapsulated ticker. n and tick() appear to be "members" of ticker, but their accessibility is the same as ticker, and they are global (if ticker is a global variable). In terms of encapsulation, this modular approach is only a little bit more ridiculous than the following one (although for some simple applications, this little bit is enough).

Copy code The 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 - this is not because n is a member of ticker, but because it is ticker that calls tick(). In fact, it would be better to write ticker.n here, because if tick() is called, it is not ticker, but something else, such as:

Copy code The code is as follows:

var func = ticker.tick;
func( );

At this time, it is actually window that calls tick(), and when the function is executed, it will try to access window.n and an error will occur.

In fact, this "open door" 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 advances by 2 each time.

Copy code The code is as follows:

var config = {
nStart:100,
step:2
}

Scope chain and closure
Look at the following code. Note that we have implemented the customization of ticker by passing in config.

Copy code The code is as follows:

function ticker(config){
var n = config.nStart;
function tick(){
n = config.step;
}
}
console.log(ticker.n); // ->undefined

You may be wondering, why did ticker change from an object to a function? This is because only functions have scope in JavaScript, and variables inside the function cannot be accessed from outside the function body. Accessing ticker.n outside ticker() will result in undefined , but accessing n within tick() will have no problem. From tick() to ticker() to the 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 covers tick(). There are two solutions:

•1) Will need to call the method as the return value, just as we use the method of incrementing n as the return value of ticker();
•2) Set the variables of the outer scope, just as we did in ticker() Set getN in .

Copy code The 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

Please see, at this time, the variable n is in a "closure" and cannot be directly accessed outside ticker(), but it can be observed or manipulated through two methods.

In the first piece of code in this section, after the ticker() method is executed, n and tick() are destroyed until the next time the function is called; but in the second piece of code, ticker() After execution, n will not be destroyed because tick() and getN() may access it or change it, and the browser will be responsible for maintaining n. My understanding of "closure" is: a mechanism used to ensure that n, a variable that is within the function scope, needs to be maintained after the function is executed, and may be accessed through other methods, is not destroyed.

But, I still feel something is wrong? What if I need to maintain two objects ticker1 and ticker2 with the same functionality? There is only one ticker(), so we can’t write it again, right?

new operator and constructor
If you call a function through the new operator, a new object will be created and the function will be called using that object. In my understanding, the construction process of t1 and t2 in the following code is the same.

Copy code The code is as follows:

function myClass(){}
var t1 = new myClass();
var t2 = {};
t2.func = myClass;
t2.func();
t2.func = undefined;

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

Copy code The 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

By convention, constructors are capitalized. Note that TICKER() is still a function, not a pure object (the reason why we say "pure" is that functions are actually objects, and TICKER() is a function object). The closure is still valid and we cannot access ticker1.n .

Prototype prototype and inheritance
The above TICKER() still has flaws, that is, ticker1.tick() and ticker2.tick() are independent of each other! Please see, every time you use the new operator to call TICKER(), a new object will be generated and a new function will be generated to bind to this new object. Every time a new object is constructed, the browser will open up a space. Storing tick() itself and variables within tick() is not what we expect. We expect ticker1.tick and ticker2.tick to point to the same function object.

This requires the introduction of prototypes.

In JavaScript, except for Object objects, other objects have a prototype property, which points to another object. This "another object" still has its prototype object and forms a prototype chain, which ultimately points to the Object object. When calling a method on an object, if it is found that the object does not have a specified method, then search for this method on the prototype chain until the Object object.

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

The prototype of a new object constructed through a constructor points to the prototype object of the constructor. So we can add functions to the prototype object of the constructor, and these functions will not depend on ticker1 or ticker2, but on TICKER.

You might do this:

Copy code The 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;
};

Please note that this is an invalid implementation. Because the methods of the prototype object cannot access the contents of the closure, that is, the variable n. After the TICK() method is run, n can no longer be accessed, and the browser will destroy n. In order to access the contents of the closure, the object must have some concise instance-dependent methods to access the contents of the closure, and then define complex public methods on its prototype to implement the logic. In fact, the tick() method in the example is concise enough, let's put it back into TICKER. Next implement a more complex method tickTimes() that will allow the caller to specify the number of times tick() is called.

Copy code The 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 great. It encapsulates n and cannot directly change it from outside the object, and the complex function tickTimes() is defined on the prototype. This function operates on the data in the object by calling small functions of the instance.

So, in order to maintain the encapsulation of the object, my suggestion is to decouple the operation of the data into the smallest possible unit function, defined in the constructor as instance-dependent (also called " "Private"), and implement complex logic on the prototype (ie "Public").

Finally, let’s talk about inheritance. In fact, when we define a function on the prototype, we are already using inheritance! Inheritance in JavaScript is more...well...simple, or crude, than in C. In C, we may define an animal class to represent an animal, and then define a bird class to inherit the animal class to represent a bird, but what I want to discuss is not such inheritance (although such inheritance can also be implemented in JavaScript); I want to The discussion of inheritance in C would be to define an animal class and then instantiate a myAnimal object. Yes, this is instantiation in C, but is treated as inheritance in JavaScript.

JavaScript does not support classes. The browser only cares about the objects currently available and does not bother to worry about what classes these objects are and what structure they should have. In our example, TICKER() is a function object. We can assign a value to it (TICKER=1) and delete it (TICKER=undefined). However, because there are currently two objects, ticker1 and ticker2, through the new operator When it is called, TICKER() acts as a constructor, and the TICKER.prototype object acts as a class.

The above is the method of JavaScript modularization that I know. If you are also a beginner, I hope it will be helpful to you. If there is something wrong, please point it out.

Author: Master Yiyezhai
Source: www.cnblogs.com/yiyezhai

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn