Prototype chain inheritance
The principle of
The essence is to rewrite the prototype object and replace it with an instance of a new type. In the following code, the properties and methods that once existed in the SuperType instance object now also exist in subType.prototype.
implementation
function Super(){
this.value = true;
}
Super.prototype.getValue = function(){
return this.value
}
function Sub(){};
// Sub inherits Super
Sub.prototype = new Super();
Sub.prototype.constructor = Sub;
const ins = new Sub();
console.log(ins.getValue()); // true
Copy the code
Sub inherits Super, which is achieved by creating an instance of Super and assigning it to sub. prototype. All properties and methods that used to exist in instances of Super now also exist in sub.prototype. As shown in the figure.
As you can see from the above image, instead of using the default prototype provided by Sub, we have replaced it with a new prototype; This new prototype is an example of Super. Thus, the new stereotype not only has the properties and methods that it has as an instance of Super, but it also points to the stereotype of Super. The end result looks like this:
Ins =>Sub prototype =>Super prototypeCopy the code
The getValue() method is still in sub.prototype, but the value property is in sub.prototype. This is because value is an instance property and getValue() is a prototype method. Since sub. prototype is now an instance of Super, value is in that instance.
Also, note that ins.constructor now points to Super because the original sub. prototype constructor was overwritten.
disadvantages
- Private stereotype attributes are shared by the instance
- When creating an instance of a subtype, arguments cannot be passed to the constructor of the parent type
The main problem with stereotype chain inheritance is that private stereotype attributes are shared by instances, which is why you define attributes in constructors rather than stereotype objects. When inheritance is implemented through stereotypes, the stereotype instance becomes an instance of another class. Thus, the original instance property becomes the prototype property of course.
function Super(){
this.colors = ['red'.'green'.'blue'];
}
Super.prototype.getValue = function(){
return this.colors
}
function Sub(){};
//Sub inherits Super
Sub.prototype = new Super();
const ins1 = new Super();
ins1.colors.push('black');
console.log(ins1.colors);//['red','green','blue','black'];
const ins2 = new Sub();
console.log(ins2.colors);//['red','green','blue','black'];
Copy the code
The second problem with stereotype chains is that you cannot pass arguments to the constructor of the parent type when creating an instance of a subtype. In fact, there is no way to pass arguments to the constructor of the parent type without affecting all instances. Coupled with the problem that stereotype attributes containing reference type values are shared by all instances, stereotype chain inheritance alone is rarely used in practice
Pay attention to the problem
Methods are defined carefully with stereotype chain inheritance. Subtypes sometimes need to override a method of the parent class, or add a method that doesn’t exist in the parent class. However, the code that adds methods to the stereotype must come after the statement that replaces the stereotype.
function Super() {
this.colors = ['red'.'green'.'blue'];
}
Super.prototype.getValue = function() {
return this.colors
}
function Sub() {
this.colors = ['black'];
};
//Sub inherits Super
Sub.prototype = new Super();
// Adding an existing method to the parent overrides the parent method
Sub.prototype.getValue = function() {
return this.colors;
}
// Add a method that does not exist in the parent class
Sub.prototype.getSubValue = function(){
return false;
}
const ins = new Sub();
// The result of overwriting the parent method
console.log(ins.getValue()); //['black']
// The result of the newly defined method in the subclass
console.log(ins.getSubValue());//false
// The parent class calls getValue() with the same value
console.log(new Super().getValue());//['red', 'green', 'blue']
Copy the code
Borrow constructor inheritance
The principle of
Borrow constructors (sometimes called pseudo-class inheritance or classical inheritance). The basic idea of this technique is fairly simple: call the superclass constructor inside the subclass constructor. Remember that functions are simply objects that execute code in a particular environment, so you can also execute constructors on newly created objects by using the Apply () and call() methods.
implementation
function Super() {
this.colors = ['red'.'green'.'blue'];
}
Super.prototype.getValue = function(){
return this.colors;
}
function Sub(){
// Inherits Super
Super.call(this);// Replace this in the constructor Super with an INS instance object, so that only the private properties defined in Super are inherited, not the public methods defined in the stereotype properties
}
const ins = new Sub();
console.log(ins.colors);
Copy the code
Passing parameters: Borrowing constructor inheritance has a big advantage over prototype chains in that you can pass parameters to the parent class constructor in the subclass constructor
function B(name){
this.name = name;
}
function A(){
// Inherits B, passing arguments
B.call(this.'ZZ');
// Instance properties
this.age = 100;
}
const p = new A();
alert(p.name);//'ZZ'
alert(p.age);/ / 100
Copy the code
disadvantages
If you just borrow constructors, you can’t avoid the problem of the constructor pattern — methods are defined in constructors, so function reuse is out of the question. Also, methods defined in the parent class’s prototype are not visible to subclasses, so this approach is rarely used.
Combination of inheritance
The principle of
Combinatorial inheritance refers to an inheritance pattern that combines a stereotype chain with a borrowed constructor technique to exploit the best of both. The idea behind this is to use stereotype chains to implement inheritance of public attributes and methods on stereotypes, while borrowing constructor inheritance to implement inheritance of private attributes on parent classes. In this way, function reuse is achieved by defining methods on the parent class prototype, while ensuring that each instance has the private attributes of the parent class.
implementation
function Super(name){
this.name = name;
this.colors = ['red'.'blue'.'green'];
}
Super.prototype.sayName = function(){
alert(this.name);
}
function Sub(name,age){
Super.call(this,name);
this.age = age;
}
// Inheritance method
Sub.prototype = new Super();
Sub.prototype.constructor = Sub;
Sub.prototype.sayAge = function(){
alert(this.age);
}
const ins = new Sub('jarvis'.18);
ins.colors.push('black');
console.log(ins.colors);// ["red", "blue", "green", "black"]
ins.sayName();//'jarvis'
ins.sayAge();/ / 18
const ins2 = new Sub('ershiyi'.21);
console.log(ins2.colors);//["red", "blue", "green"]
ins2.sayName();//'ershiyi'
ins2.sayAge();/ / 21
Copy the code
In the previous example, the Sub constructor defines two attributes: name and age. The Super prototype defines a sayName() method. The Super constructor is called in the Sub constructor with the name argument, followed by the definition of its own property age. We then assign an instance of Super to the prototype of Sub, and define the method sayAge() on that new prototype. In this way, different Sub instances can have their own attributes — including the colors attribute — and you can use the same method to combine inheritance to avoid the pitfalls of stereotype chains and borrowed constructors and combine their advantages. This is called the most common inheritance pattern in JavaScript.
disadvantages
In any case, the superclass constructor is called twice: once when the subclass prototype is created and once inside the subclass constructor.
Parasitic combinatorial inheritance
The principle of
Composite inheritance is the most common inheritance pattern in JavaScript. However, it has its own disadvantages. The biggest problem with composite inheritance is that in any case, the superclass constructor is called twice: once when the subclass stereotype is created and once inside the subclass constructor. Yes, subtypes end up containing all of the instance properties of the supertype object, but they have to be overridden when the subtype constructor is called. Take a look at the following composite inheritance example.
implementation
function Super(name){
this.name = name;
this.colors = ['red'.'blue'.'green'];
}
Super.prototype.sayName = function(){
alert(this.name);
}
function Sub(name,age){
Super.call(this,name);
this.age = age;
}
// Inheritance method
Sub.prototype = new Super();
Sub.prototype.constructor = Sub;
Sub.prototype.sayAge = function(){
alert(this.age);
}
const ins = new Sub('jarvis'.18);
ins.colors.push('black');
console.log(ins.colors);// ["red", "blue", "green", "black"]
ins.sayName();//'jarvis'
ins.sayAge();/ / 18
const ins2 = new Sub('ershiyi'.21);
console.log(ins2.colors);//["red", "blue", "green"]
ins2.sayName();//'ershiyi'
ins2.sayAge();/ / 21
Copy the code
Parasitic combinatorial inheritance inherits properties by borrowing constructors and inherits methods through a hybrid form of prototype chains. The basic idea behind this is that you don’t need to call the constructor of the supertype to specify the stereotype of the subtype; all you need is a copy of the stereotype of the supertype. Essentially, you use parasitic inheritance to inherit the stereotype of the supertype and then assign the result to the stereotype of the subtype. The basic pattern of parasitic combinatorial inheritance is shown below.
function Super(name){
this.name = name;
this.colors = ['red'.'blue'.'green'];
}
Super.prototype.sayName = function(){
alert(this.name);
}
function Sub(name,age){
// Inherit instance attributes
Super.call(this,name);
this.age = age;
}
// Inherit public methods
Sub.prototype = Object.create(Super.prototype);
Sub.prototype.constructor = Sub;
Sub.prototype.sayAge = function(){
alert(this.age);
}
const ins = new Sub('jarvis'.18);
ins.colors.push('black');
console.log(ins.colors);// ["red", "blue", "green", "black"]
ins.sayName();//'jarvis'
ins.sayAge();/ / 18
const ins2 = new Sub('ershiyi'.21);
console.log(ins2.colors);//["red", "blue", "green"]
ins2.sayName();//'ershiyi'
ins2.sayAge();/ / 21
Copy the code
Multiple inheritance
Multiple inheritance does not exist in JavaScript, which means that an object cannot inherit multiple objects at the same time, but workarounds can be used to do this.
<! DOCTYPEhtml>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="Width = device - width, initial - scale = 1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>18 Multiple Inheritance</title>
</head>
<body>
<script type="text/javascript">
// Multiple inheritance: an object inherits multiple objects simultaneously
// Person Parent Me
function Person(){
this.name = 'Person';
}
Person.prototype.sayName = function(){
console.log(this.name);
}
/ / customize the Parent
function Parent(){
this.age = 30;
}
Parent.prototype.sayAge = function(){
console.log(this.age);
}
function Me(){
// Inherit the attributes of Person
Person.call(this);
Parent.call(this);
}
// Inherit the Person method
Me.prototype = Object.create(Person.prototype);
// You cannot override a prototype object to implement inheritance from another object
// Me.prototype = Object.create(Parent.prototype);
// Object.assign(targetObj,copyObj)
Object.assign(Me.prototype,Parent.prototype);
// Specify the constructor
Me.prototype.constructor = Me;
const me = new Me();
</script>
</body>
</html>
Copy the code
ES5 and ES6 inheritance differences
In traditional inheritance of ES5, the value of this is created by the derived class before the base class constructor is called. This means that this is initially an instance of a derived class, of which
It is then decorated with additional attributes from the base class.
In ES6 class-based inheritance, the value of this is created by the base class before being modified by the constructor of the derived class. The result is that this initially has all of the functionality of the built-in object as a base class, and correctly receives all of the functionality associated with it.