JavaScript is an object-oriented language without classes. It uses prototype inheritance to replace class inheritance. JavaScript Inheritance
DouglasCrockford
Www.crockford.com
And you think you're so clever and classless and free
-- John Lennon
JavaScript is an object-oriented language without classes. It uses prototype inheritance to replace class inheritance. This may be a bit confusing for programmers trained in traditional object-oriented languages (such as C ++ and Java. JavaScript prototype inheritance is more expressive than class inheritance. Let's take a look at it now.
Java |
JavaScript |
Strong type |
Weak type |
Static |
Dynamic |
Class-based |
Prototype-based |
Class |
Function |
Constructor |
Function |
Method |
Function |
But first, why are we so concerned about inheritance? There are two main reasons. The first type is advantageous. We hope that the language system can automatically convert similar types of references.Cast. Small-type security can be obtained from a type system that requires the program to display and convert object references. This is the most critical aspect of a strong-type language, but it is irrelevant to a weak-type language like JavaScript. Class references in JavaScript do not need to be forcibly converted.
The second reason is to reuse the code. In programs, we often find that many objects implement the same method. Class makes it possible to create an object in a single definition set. It is also common to include other objects in an object, but the difference is only a small part of method addition or modification. Class inheritance is very useful for this, but prototype inheritance is even more useful.
To demonstrate this, we want to introduce a small "dessert" that allows us to write code like a common class language. We will then demonstrate some useful patterns that are not available in the class language. Finally, we will explain these "desserts ".
Class inheritance
First, we create a Parenizor class, which has get and set methods for Member values, and a toString method that encapsulates value in parentheses.
The Code is as follows:
Function Parenizor (value ){
This. setValue (value );
}
Parenizor. method ('setvalue', function (value ){
This. value = value;
Return this;
});
Parenizor. method ('getvalue', function (){
Return this. value;
});
Parenizor. method ('tostring', function (){
Return '(' + this. getValue () + ')';
});
This syntax may be useless, but it is easy to see the form of the class. The method accepts a method name and a function, and puts them into the class as a public method.
Now we can write
The Code is as follows:
MyParenizor = new Parenizor (0 );
MyString = myParenizor. toString ();
As expected, myString is "(0 )".
Now we need to create another class inherited from Parenizor. It is basically the same except that the toString method will generate "-0-" if the value is zero or null.
The Code is as follows:
Function ZParenizor (value ){
This. setValue (value );
}
ZParenizor. inherits (Parenizor );
ZParenizor. method ("e; toString" e;, function (){
If (this. getValue ()){
Return this. uber ('string ');
}
Return "-0 -";
});
The inherits method is similar to Java extends. The uber method is similar to the Java super method. It allows a method to call the method of the parent class (the name is changed to avoid conflicts with reserved words ).
We can write it like this
The Code is as follows:
MyZParenizor = new ZParenizor (0 );
MyString = myZParenizor. toString ();
This time, myString is "-0 -".
JavaScript does not have classes, but we can program to achieve this goal.
Multi-Inheritance
By operating on the prototype object of a function, we can implement multi-inheritance. Hybrid multi-inheritance is difficult to implement and may be at risk of Name Conflict. We can implement mixed multi-inheritance in JavaScript, but in this example we will use a more standard form called Swiss inheritance SwissI nheritance.
Suppose there is a NumberValue class with a setValue method to check whether the value is a number within a specified range, and throw an exception when appropriate. We only need its setValue and setRange methods to give our ZParenizor. Of course we do not want its toString method. In this way, we write:
The Code is as follows:
ZParenizor. swiss (NumberValue, 'setvalue', 'setrange ');
This will only add the required method.
Parasitic inheritance
This is another method for writing the ZParenizor class. Instead of inheriting from Parenizor, it writes a constructor that calls the Parenizor constructor and returns the result after modifying the result. This constructor adds privileged methods instead of public methods.
The Code is as follows:
Function ZParenizor2 (value ){
Var self = new Parenizor (value );
Self. toString = function (){
If (this. getValue ()){
Return this. uber ('string ');
}
Return "-0 -"
};
Return self;
}
Class inheritance is a "Yes ......" And parasitic inheritance is about "the original ...... But now ......" . The constructor plays a large number of roles in object construction. Note that uber (instead of the super keyword) is still valid for privileged methods.
Class Extension
The dynamic nature of JavaScript allows us to add or replace methods for an existing class. We can call methods at any time. We can extend a class at any time. This method is not used for inheritance. So we call this a "class extension" to avoid confusion with Java extends, also called extensions, but not the same thing.
Object Extension
In static object-oriented language, if you want an object to be different from another object, you must create a new class. However, in JavaScript, you can add methods to individual objects without creating new classes. This will have a huge amount of energy, because you can write as few classes as possible, and classes can be written more easily. Think about JavaScript objects like hash tables. You can add new values at any time. If this value is a function, it will become a method.
In this case, the ZParenizor class is not required. I just need to modify my instance.
The Code is as follows:
MyParenizor = new Parenizor (0 );
MyParenizor. toString = function (){
If (this. getValue ()){
Return this. uber ('string ');
}
Return "-0 -";
};
MyString = myParenizor. toString ();
We added a toString method to the myParenizor instance without any inheritance. We can evolve a separate instance because the language is of no type.
Dessert
To let the above example run, I wrote four "dessert" methods. First, you can add an instance method to a class.
The Code is as follows:
Function. prototype. method = function (name, func ){
This. prototype [name] = func;
Return this;
};
This will add a public method to Function. prototype, so that all functions can be used through class extension. It requires a name and a function as a parameter.
It returns this. When I write a method that does not return a value, I usually make it return this. In this way, a chain statement can be formed.
The following is the inherits method, which indicates that a class inherits from another class. It can be defined only after both classes are defined, but must be called before methods are inherited.
The Code is as follows:
Function. method ('explores', function (parent ){
Var d = 0, p = (this. prototype = new parent ());
This. method ('uber ', function uber (name ){
Var f, r, t = d, v = parent. prototype;
If (t ){
While (t ){
V = v. constructor. prototype;
T-= 1;
}
F = v [name];
} Else {
F = p [name];
If (f = this [name]) {
F = v [name];
}
}
D + = 1;
R = f. apply (this, Array. prototype. slice. apply (arguments, [1]);
D-= 1;
Return r;
});
Return this;
});
Let's extend the Function class. We add a parent class instance and use it as the new prototype. We must also modify the constructor field and add the uber method.
The uber method looks for a method in its prototype. This is a case of parasitic inheritance or class extension. If we use class inheritance, we need to find the function in parent's prototype. The return Statement calls the apply method of the function to call the function, sets this explicitly, and passes parameters. Parameters (if any) can be obtained from the arguments array. Unfortunately, the arguments array is not a real array, so we need to use apply to call the slice Method in the array.
Finally, the swiss Method
The Code is as follows:
Function. method ('Swiss ', function (parent ){
For (var I = 1; I <arguments. length; I + = 1 ){
Var name = arguments [I];
This. prototype [name] = parent. prototype [name];
}
Return this;
});
The swiss method loops every parameter. Each name copies the members in the prototype of the parent to the prototype of the new class.
Summary
JavaScript can be used as a class language, but it also has a very unique expression layer. We have seen class inheritance, Swiss inheritance, parasitic inheritance, class extension, and object extension. This first-class code reuse model can all come from this small, very simple JavaScript language.
Class objects belong to "hard ". The only way to add members to a "hard" object is to create a new class. In JavaScript, the object is "soft ". To add members to a "soft" object, simply assign values.
Because the classes in JavaScript are so flexible, you may think of more complex class inheritance. However, deep inheritance is not suitable. Shallow inheritance is more effective and easier to express.