JS Beginners are mostly not aware of its powerful object-oriented programming features, just JS as a simple and practical scripting language to use. Because of this, JS programmers are often in the programmer despise the lowest end of the chain, many people think that JS is a language of HTML, and even the language is not called. The fact that this is not the case, if you have this idea, it means that your understanding of JS is too shallow. To really step into the door of JS, you have to understand the characteristics of JS object-oriented programming. Let me give you a word.
First, create the object
Since it is object-oriented, it must have objects first, to have objects, it must know what the object is, that JS object is what? In C + + we know that an object is an instance of a class or struct, and an object is instantiated by its template. But JS doesn't even have a class, how does it define the object? Quite simply, the object in JS is a large pile of key-value pairs enclosed in curly braces. The key is called the object's property name, the theory is the string type, but actually you do not add the quotation mark does not matter, because JS to the data type concept is so capricious; The value is the property value of the object property name, the property value can be the five basic data types, can also be another object, so that objects can have objects, Can create a colorful JS object. OK, after you know what the object is, let's start making an object.
Method One: Object ()
Method Two: Object literal
1 var New Object (); 2 bitch.boobs = ' huge '; 3 bitch[' bf '] = {name:jhon,age:22};
This example uses method one and method second to create the object, both of which are very basic, but there are still two points to mention: one, access to object properties in two forms,. and []. The former is relatively simple, because it does not have to be quoted in the attribute name, the latter of course has its advantage, that is, when the property name has a space in the special characters, the former does not work, then the latter is the world. Second, when invoking the object () constructor, the new operator can actually be omitted, which can be generalized to many other constructors, but with two exceptions: String () and number (). For these two functions, if you do not add new, just do the conversion of the data type once, you will get the value of the basic data type, and with the new operator, you will get an instance of String/number.
These two methods are simple and intuitive to create objects, but there are also problems, that is, the object can not be mass production. As a result, the factory model emerged.
Method Three: Factory mode (note the factory pattern that differs from the design pattern)
function Bitch (boobs,bfname,bfage) { varnew Object (); = boobs; = {name:bfname,age:bfage}; function () {Console.log (' crying ');} return bitch;} Bitch=bitch (' huge ', ' Jhon ', 22);
In effect, you use a function to encapsulate the specifics of a particular pretext, avoiding too much duplication of code when you mass-produce objects. The factory model solves the problem of mass production of objects, but there is still one problem that is not solved-the problem of object identification. That is, the objects created are independent individuals, not a dime relationship with other objects, even objects created with the same function are not known to each other, and this is obviously not what we want. Thus, the constructor pattern arises.
Method Three: Constructor mode
function Bitch (boobs,bfname,bfage) { this. boobs=boobs; this. bf={name:bfname, age:bfage}; This function () {Console.log (' crying ');}} Bitch=new bitch (' huge ', ' Jhon ', 22);
The characteristic of the constructor pattern can be found in the comparison of the Factory mode: First, the object is not displayed, but it is directly assigned to the this pointer; two, there is no return statement; In fact, the most important thing is that the new operator, if not add this new, the constructor is a normal function, and any ordinary function in the invocation of the new operator before the word will become a constructor. Under the new operator, the constructor executes roughly as follows: Create a new Object---> Assign this object to the this pointer---> execute the code in the function---> Return this object (note that there is no constructor for any return value here, If the constructor shows a value returned, it will be different, and I'll explain it in detail in the JavaScript constructor article. As mentioned earlier, the constructor pattern exists to solve the problem of object recognition in Factory mode, does this solve the problem?
instanceof // true instanceof // true // true
As the above code shows, object recognition is fine, but then there is a problem--all of the object's function properties are recreated on each object, and these functions are actually identical, so it's not throwaway. Therefore, the constructor needs to be improved. Here's an improved method:
function Cry () {Console.log (' crying ');} function Bitch (boobs,bfname,bfage) { this. boobs=boobs; this. bf={name:bfname, age:bfage}; this. Cry = cry;} Bitch=new bitch (' huge ', ' Jhon ', 22);
At first glance, this approach solves the problem well, but in fact there is a fatal disadvantage--you write the object's function attributes to the global function, the global environment is not you mercilessly defiled? It seems that the method is pass, then how to solve the function on each object repeatedly created problem? Prototype Mode is born!
Method Four: Prototype mode
function Bitch () {}; Bitch.prototype.boobs = ' huge ' ; BITCH.PROTOTYPE.BF ={name: ' John ', Age:22}bitch.prototype.cry =function () {Console.log (' crying ');}
This is the so-called prototype pattern. Of course, to understand this code you have to figure out what a prototype is. To be blunt, a prototype is simply a property. But instead of our custom properties, the parser automatically adds an attribute to the function when we define the function, which is the inherent property of the function. The name of this property is called prototype, and the attribute value is an object, and we are going to do it for the object named prototype. Looking at the code above, we have added all the objects that should have been added inside the constructor to the this pointer to the function's prototype object. Because when we call the constructor to construct the object, the constructed object holds a reference to the constructor prototype object. Moreover, when we access a property of the object, the JS parser asks the object first: "Hey, do you have this attribute?" "If there is, well, straight back; if not, the parser will not let it go, but instead, find the prototype object based on a reference to the prototype object of its constructor, and ask:" Hey, do you have this attribute ", if there is, well, go straight back; Instead of looking for the prototype object's constructor (it feels very much like this, it's very simple to remember that the prototype object is the property of the constructor, not the property of the object constructed by the constructor, the object that the constructor constructs just knows where to find the prototype object), And then keep making unremitting efforts until you find this attribute on a prototype object or touch the head of the prototype chain. Naturally, we elicit the concept of the prototype chain. The prototype chain appears because the object has a corresponding prototype object, and the prototype object is also the object, it also has its own corresponding prototype object, which soon formed a prototype chain? Where is the head of the prototype chain? The head of the prototype chain is in Object.prototype. The Object.prototype itself is an object, but this object is a bit special because it does not have a corresponding prototype object.
Ok, pulled a big paragraph, around a bit dizzy, at first I also do not understand, but see more nature will understand. Back to the code above, we add properties to the prototype object, and when we construct the object, we actually get an empty object, but we can access its related properties, because the parser will not be able to find the prototype object. But the prototype object has only one (or the above sentence, the prototype object is the property of the constructor), no matter how many objects we build, we do not create the property repeatedly-that is, the problem of the constructor pattern is well resolved. However, the prototype object brings up a problem-all properties are the same, and the object I created is not a bit of a personality. This is really a problem, but the problem is easy to solve-the combination of the constructor pattern and the prototype model, the former responsible for personality, the latter is responsible for the common, the two complement each other. The final code looks like this, which is the final code, and there are actually many patterns of creating objects, but the application is not extensive and is not described here:
function Bitch (boobs,bfname,bfage) { this. boobs=boobs; this. bf=function() {Console.log (' crying ');};
Second, private property
As mentioned earlier, we finally created the object in a more perfect way, but there are still problems--all the properties of the object are public, a little encapsulation is not you dare to pretend object-oriented programming? The following is a solution to the problem of encapsulation.
First we want to be clear, JS is not similar to public/private such as access control, JS even block-level scope is not, that JS how to achieve encapsulation? This is going to take out our next big uncle (the last one is a prototype)--closures. Where does the closure of the package divine? I personally understand that the closure is a function (this sentence starts to be controversial, but this is what I do for the sake of understanding only to make a judgment, please do not merely the word), but not the general function, but the function in the functions, let it become an intrinsic function. But this inner function is a bit of a bull, because it can freely access the variables of its external function, not only that, it also holds the oppressing of those external function variables, what does that mean? Let's take a look at an example:
function husband (money) { var car = 3, house = 2; function wife () { return {Money:money, Car:car, House:house
} ; return wife;} var wife = husband (+), assets = wife (); Console.log (assets); Object {money:1000, car:3, house:2}
In theory, when the statement wife = husband () is executed, the scope of the husband () function is destroyed, and those variables inside it should cease to exist. But the code execution results tell us that these variables are not destroyed immediately after husband () is executed, because husband () has an intrinsic function wife (), which is the closure, which causes husband () to have no oppressing in its own internal variables, Such power is now in the hands of wife. As a result, these variables are not destroyed until wife () is finished executing. This is the meaning of the previous sentence.
At the moment, we know what a closure is, and we know the power of life and death of a variable with an external function in the closure, so how do you use this feature of closures to create private properties?
function Bitch (boo,bfname,bfage) { var boobs = boo; // private property this . Bf={name:bfname, age:bfage}; // public properties this . Watchboobs = function () {return ' huge '; }; // }bitch.prototype.cry = function () { Console.log (' crying ');};
The key to this code is that the property that should remain private is not created directly on the object (because the property directly added to the object cannot be kept private), but rather simply a private variable is declared in the function, and then provided by a closure (this.watchboobs) to the external access interface of the private variable. In this way, the external can not directly access the boobs, only through the closure of the Watchboobs access, which can only be seen far and not to be obscene is not the characteristics of the private property? At this point, the private property has been successfully implemented, that is, the encapsulation has been done, the following to solve the object-oriented programming of the second major feature-inheritance.
Iii. inheritance
After a thorough understanding of the concept of prototypes and prototype chains, it is not difficult to find that prototypes are the best tool for implementing inheritance in JS. Here's a look at the specific implementation:
function Sup (a) { this. A== function () {Console.log ('function of Sup.prototype');} function Sub (A, b ) {this. A = A; this. B =new= function () {Console.log ('function of Sub.prototype');}
The essence of implementing inheritance by using prototypes is to rewrite the function's prototype. Recall what the prototype of the function is-an object (by default, the object), and how to rewrite the prototype of the function-assigning a new object to it, and what the new prototype requires-it is the object of the parent class. Thus, we have this kind of wording:
New Sup ();
Note the differences between JS inheritance and traditional oo language inheritance, such as the inheritance of C + +, is written like this:
Class A:public b{}
I think the difference is mainly two points: first, the inheritance of the traditional OO language is the inheritance of the class, is the inheritance between the abstract concept, the implementation of inheritance does not require the parent class instance, and the inheritance of JS is the inheritance of the instance, the subclass inherits one instance of the parent class. Second, the traditional OO language inheritance sub-public/private/protected and other different ways of inheritance, and JS itself even the concept of private variables are not, it is more impossible to distinguish between common inheritance and private inheritance (that JS can implement the class is a function?) How do we do that? )。
The way JS implements inheritance is called "prototype Mode", which has several drawbacks:
When you create a subclass instance, you cannot pass parameters to the constructor of the parent class, such as the traditional OO language:
A:A (int A,int b) { b:b (b); this. A = A;}
)
Second, when the parent class attribute has a reference type value, it can cause a fatal problem. For several examples:
functionSup (a) { This. A =A;} Sup.prototype.foo=function() {Console.log (' Function of Sup.prototype ');}functionSub (b) { This. B =b;} Sub.prototype=NewSup ([]); Sub.prototype.bar=function() {Console.log (' Function of Sub.prototype ');}varSub1 =NewSub (1), Sub2=NewSub (2); Sub1.a.push (4); Console.log (sub2.a); //The changes to Sub1 have affected the SUB2.
As I said earlier, the inheritance of JS actually inherits an instance of the parent class. When a property of the parent instance is a reference-type value, and we call some methods on the value to change the value, the instance of all subclasses is affected. Note, however, that this is the way to change the value of this reference type (Sub1.a.push (4)) If I use the following method:
sub2.a = Null;console.log (sub1.a); Sub1 not affected
This is because the direct assignment of a property to an attribute is actually a dynamically added property on the instance of the subclass, and the property of the prototype object is overridden, so the properties of the prototype object are overwritten, so the other subclass instances are not actually affected, which is a clear distinction.
OK, "prototype mode" shortcomings have been finished, then how to solve it? Very simple, the same as the creation of objects, the use of the constructor mode, the two together, to learn from each other. The complete implementation is as follows:
function Sup (a) { this.a =function() {Console.log (' function of Sup.prototype ');} function Sub (A, b) { Sup.call (this, a ); this. B =new=function() {Console.log (' function of Sub.prototype ');}
It seems to be perfect, but there are two questions to consider:
One, Sup.call (this,a);
This solves the problem of not passing arguments to the constructor of the parent class when an instance of the subclass is created in prototype mode, and resolves an issue in which the child class instances might be affected by a property that has a reference type value on the prototype object (because the parent class's properties are all copied over on the subclass, the Parent property is overwritten). But in this way, subclasses extend inconvenient when we want to extend the parent class, such as when the parent class becomes a function Sup (a, B). So, here's an improved notation (the idea I've been told by an interviewer):
Sup.apply (this, arguments);
Second, Sub.prototype.construtor = Sub;
Is this sentence necessary to write? Still the interviewer, who thought it was necessary to write it. Because if this is not the case, there will be a problem when judging the instance type with the instanceof operator, that is, the Sub1 instanceof Sub will return false. But testing on chrome, I found that even without this sentence, the Sub1 instanceof Sub would return true. JS elevation is a book that returns true as long as it is a constructor that appears in the prototype chain. So I think the interviewer's statement is wrong, and the only benefit of this sentence is that Sub1.constructor will point to Sub.
Ok, wrote three days to finally finish this article. Basically JS oop all aspects of the basic concepts are explained clearly, for others is a very good technology sharing, to oneself is a good knowledge review.
A probe into the knowledge points triggered by several JS pen questions 15--js object-oriented programming