Let’s talk about inheritance
There may be many people who know how to implement inheritance, but don’t know how to implement inheritance. I, for one, can’t remember and use this knowledge properly without understanding how it works. A question or an interview, there will be all kinds of instability, all kinds of meng tree meng fruit meng tree you and me. It turns out that drawing this inheritance is a great help in learning about it. I wanted to write it down. (I have absorbed various knowledge and transformed it into my own understanding. If there is any misunderstanding or deviation, please correct me.)
When it comes to inheritance, it is inevitable to leave the prototype and prototype chain. If you are not familiar with this knowledge, I recommend reading this article, which is brief and easy to understand.
As for inheritance, I think the core is two points: 1. Constructor inherits properties; 2
ES5 inheritance
Constructor, prototype, and instance relationships:
Each constructor (function object) has a prototype property that points to the function’s prototype object; Each stereotype object has a constructor property pointing to the constructor; Each instance has a __proto__ attribute that points to the constructor’s prototype object
1. Prototype chain inheritance
Implementation essence: Rewrite the prototype object and replace it with an instance of a new type
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function(){
return this.property;
}
function SubType(){
this.subproperty = false;
}
// Inherits SuperType. The SubType stereotype is overridden to be equal to an instance of superType (as an instance of the parent class, so it has the attributes and methods of the parent class), implementing stereotype chain inheritance
SubType.prototype = new SuperType();
SubType.prototype.getSubValue =function(){
return this.subproperty;
}
var instance = new SubType();
alert(instance.getSuperValue());//true
Copy the code
A: Rewrote the SubType stereotype so that the SubType stereotype disconnects from the default subclass stereotype (of course, the constructor of the new subclass stereotype points to the parent stereotype); A subclass stereotype is an instance of a parent class such that its __proto__ points to the parent. (Because the instance’s __proto__ points to the constructor prototype object)
B: A subclass can access the getSuperValue method of its parent class along the prototype chain by linking its __proto__ to its parent class.
C: Because the subclass stereotype is an instance of the parent class, the subclass stereotype inherits the attributes of the parent class through the constructor of the parent class (the subclass inherits the attributes of the parent class)
Existing problems:
1. Stereotypes that contain reference type values: Instance properties of the parent class become properties of the subclass, which are shared by all instances of the subclass. If this value is the basic type, no problem, but if it is a reference type (array, is a pile of storage, the same memory address references), if the instance 1 modify the properties (such as a push into the array a value, also changed is the prototype of a reference type attribute values), instance 2 of the value of this attribute will also change, Later, the newly created instance also gets the value of the latest reference type.
function SuperType(){
this.colors =['red'.'blue'.'pink'];
}
function SubType(){}
SubType.prototype = new SuperType();
var instance1 = new SubType();
var instance2 = new SubType();
instance1.color.push('skyblue');
console.log(instance1.colors) //['red','blue','pink','skublue']
console.log(instance2.colors) //['red','blue','pink','skublue']
var instance3 = new SubType();
console.log(instance3.colors) //['red','blue','pink','skublue']
Copy the code
2. When creating a subclass instance, you cannot pass arguments to the constructor of the parent class.
Because of these problems, prototype chain inheritance is rarely used in practice alone
Constructor inheritance
function SuperType(name){
this.name = name;
this.colors = ['red'.'blue'.'green'];
}
function SubType(){
// Inherit SuperType(inherit attributes)
// The advantage here is that the subclass constructor can now pass arguments to the superclass constructor
SuperType.call(this.'boom shakalaka');// Functions are just objects that execute code in a particular environment, so you can execute constructors on new objects by using the Apply or Call methods
// This step is equivalent to the parent class constructor code run through here
//this.name = 'boom shakalaka';
//this.colors = ['red','blue','green'];
}
var instance1 = new SubType();
instance1.colors.push('black')
console.log(instance1.name); //'boom shakalaka'
console.log(instance1.colors); //['red','blue','green','black'];
var instance2 = new SubType();
console.log(instance2.name); //'boom shakalaka'
console.log(instance2.colors); //['red','blue','green'];
Copy the code
Supertype.call (this,’boom shakalaka’)
A: The subclass constructor borrows the parent class constructor. When a subclass instance is created, the subclass constructor is executed (including the code for the parent constructor). This way, when a new instance is created, all of the object initialization code defined in the parent constructor will be executed, so each subclass instance will have a copy of its reference type value, thus completing inheritance.
Existing problems:
1. According to the diagram, it is clear that the subclass and the superclass prototype do not create any connection, so the subclass instance cannot access the properties and methods on the superclass prototype.
2. Methods are defined in constructors (methods in the parent class prototype are not accessible), so function reuse is not possible. For example, if there is a getColors method in the parent class, when creating subclass instances, a new getColors method is created for each instance, instance1.getColors! ==instance2.getColors
Given these problems, the technique of borrowing constructors is also rarely used in isolation
Combinatorial inheritance
function SuperType(name){
this.name = name;
this.colors = ["red"."blue"."green"];
}
SuperType.prototype.sayName = function(){
alert(this.name);
}
function SubType(name,age){
// Inherit attributes
SuperType.call(this,name); // Call SuperTyper() for the second time
this.age = age;
}
// Inheritance method
SubType.prototype = new SuperType(); // Call SuperTyper() for the first time
SubType.prototype.constructor = SubType;// We overwrote subtype. prototype, so we need to assign the constructor of the new prototype object back
SubType.prototype.sayAge = function(){
alert(this.age);
}
var instance1 = new SubType("Nicholas".29);
instance1.colors.push("black");
alert(instance1.colors); // "red","blue","green","black"
instance1.sayName(); //"Nicholas"
instance1.sayAge(); / / 29
var instance2 = new SubType("Greg".27);
alert(instance2.colors); // "red","blue","green"
instance2.sayName(); //"Greg"
instance2.sayAge(); / / 27
Copy the code
Basic idea: Inheritance of stereotype attributes and methods is implemented using stereotype chains (B), and inheritance of attributes is implemented by borrowing constructors (A). This enables function reuse by defining methods on prototypes and ensures that each instance has its own attributes
C: If the instance has its own attributes, it will not follow the prototype chain to look up the attributes on the prototype, which is equivalent to shielding the attributes of the same name on the prototype. (Don’t ask others for what you have.)
Composite inheritance is a combination of the original type and the constructor, avoiding the drawbacks of each and combining the advantages of each, so that subclass instances can have their own properties and use the same method. Become the most commonly used pattern in JS.
Problem: The parent class’s constructor is called twice. Calls made on inherited properties and inherited stereotypes produce two sets of name and colors properties, one on the instance and one on the stereotype, which can be quite wasteful.
The parasitic combinatorial inheritance below solves this problem
Primary inheritance
function object(o){ // Inside the object function, a temporary constructor appears,
// Use the passed object as a prototype for the constructor, and finally return a new instance of the temporary type
// Essentially, a shallow copy is made of the passed object
function F(){};
F.prototype = o;
return new F();
}
var person = {
name:"Nicholas".friends: ["Shelby"."Court"."Van"]};var anotherPerson = object(person);
// The new object.create () method in ES5 regulates inherited inheritance. Object.create() simply copies an Object. The object method above is essentially implemented.
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");
var yetAnotherPerson = object(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");
alert(person.friends); //"Shelby,Court,Van,Rob,Barbie"
Copy the code
Basic idea: Not strictly constructors. The new instance is modeled after the Person base object, and the attributes of Person are shared by the instance. In effect, you create two copies of Person. It’s essentially replication
The new Object. Create in ES5 standardizes old-style inheritance by taking two parameters, an Object to be used as a prototype for the new Object and (optionally) an Object to define additional properties for the new Object. Object.create() and Object () behave the same when passed a parameter.
Old-style inheritance is perfectly fine in cases where you don’t need to create constructors in a big way, but you want one object to be similar to another. The concurrent problem is consistent with stereotype chain inheritance, in that attributes that reference type values share the same value
Parasitic inheritance
function createAnother(original){
var clone = object(original);The object() function creates an object
Var clone = object.create (original
clone.sayHi = function(){ // Enhance the object (add some other methods to the object etc.)
alert("hi");
};
return clone; // Return this object
}
var person = {
name:"Nicholas";
friends:["Shelby"."Court"."Van"];
} // Base object
var anotherPerson = createAnother(person); // New object (not only has the attributes and methods of Person, but also has its own sayHi method)
anotherPerson.sayHi(); //"hi"
Copy the code
The parasitic method is the same as the original method in that it copies a base object to get a new object, except that it encapsulates the entire process (creation, enhancement, return) by putting changes to the object instance into functions as well.
Combinatorial parasitic inheritance
// Create a basic model for composite inheritance
function inheritPrototype(subType,superType){
// The argument is the subclass constructor parent class constructor
var prototype = Object.create(superType.prototype); // Create the object (create a copy of the superclass prototype)
prototype.constructor = subType; // Enhance the object (add the constructor attribute to the created copy to compensate for the loss of the default constructor attribute due to reprototyping)
subType.prototype = prototype; // Specify the object
}
function SuperType(name){
this.name = name;
this.colors = ["red"."blue"."green"];
}
SuperType.prototype.sayName = function(){
alert(this.name);
}
function SubType(name,age){
// Inherit attributes
SuperType.call(this,name); // Call SuperTyper() only once
this.age = age;
}
// Inheritance method
inheritPrototype(SubType,SuperType);
SubType.prototype.sayAge = function(){
alert(this.age);
}
Copy the code
Parasitic (copy) composite (prototype chain + constructor) inheritance, combined in a number of ways, solves the problem of calling the superclass constructor twice
Es6 inheritance
To be honest, I don’t understand how es6 inheritance is related to ES5 inheritance. Is it based on parasitic combinative inheritance? Please understand this piece of you see the big man to save the children, is the whole do not understand. Let’s write a little bit first
Inheritance from ES6 is inheritance from class, which is implemented through extends
class A{
}
class B extends A{
}
Every object in ES5 has a __proto__ attribute that points to the prototype of the corresponding constructor
Class is the syntactical sugar of the constructor and has both __proto__ and prototype properties, so there are two inheritance chains
Proto === a. protoType (as A constructor, the prototype (prototype property) of subclass (B) is an instance of the parent)
ES5 and ES6 inheritance mechanisms are different:
ES5 inheritance essentially creates an instance object of the subclass, this, and then adds the Parent class’s methods to this (parent.apply (this)). ES6 has a completely different inheritance mechanism, essentially creating an instance object of the parent class, this (so the super method must be called first), and then modifying this with the constructor of the subclass.
Parasitic combinational inheritance, I don’t know if I’ve got it right, but if I’ve got it wrong, point it out. I hope I can communicate with you and make progress together.