JavaScript language inheritance: I've always had a hard time understanding JavaScript language inheritance. It has no"Child"and"Parent"Neither does the concept of"Class"And (class)"Instance"(instance) is distinguished by a very peculiar"Prototype chain"(Prototype chain) pattern to implement inheritance. ———— Ruan Yifeng's weblogCopy the code
Concept of inheritance
What is inheritance?
In layman’s terms, the child owns the parent’s properties and methods.
Inherited methods
(I) Inheritance of prototype chain
Principle: A subclass prototype is an instance object of its parent class.
Let’s make a superclass function.
function Dad(name){}
Copy the code
Give the parent function some attributes name,age,cars.
The addCars method adds cars, and the prototype getCar method gets cars
function Dad(name,name){
this.name = name
this.age = age
this.cars = ['baoma'.'liebao']
this.addCars = function(car){
this.cars.push(car)
}
}
Dad.prototype.getCar = function(id){
return this.cars[id]
}
Copy the code
Create a new subclass function without setting any methods or properties.
function Son(){}
Copy the code
Prototype = object.prototype
Stereotype chain inheritance refers to the stereotype of a subclass pointing to an instance of its parent class.
let dad_ex1 = new Dad()// An instance of the parent function
Son.prototype = dad_ex1
Copy the code
- Only one parent class can be inherited
Son. Prototype can only refer to one instance, so only one parent class can be inherited, not multiple.
- A subclass instance cannot pass parameters to the parent constructor
Call new son_ex1, an instance of subclass Son.
let son_ex1 = new Son()
Copy the code
The __proto__ attribute of instance son_ex1 points to the prototype attribute of the constructor. The prototype:
son_ex1.__proto__ === Son.prototype
dad_ex1.__proto__ === Dad.prototype
Copy the code
The prototype of the Son constructor is an instance of the parent class: son. prototype = dad_ex1
son_ex1.__proto__.__proto__ === Dad.prototype
Copy the code
A chain of archetypal inheritance is formed.
Print the subclass instance son_ex1:
console.log(son_ex1)
Copy the code
As you can see, when we create new Son(), the Son function cannot pass arguments to the parent class, resulting in undefined name and age attributes.
- All attributes from the parent class are shared by all instances
In prototype chain inheritance, a subclass inherits the attributes and methods of its parent class by taking the private attributes and public methods of its parent class as its own public attributes and methods
Let’s make another subclass son_ex2, and we’ll add a car to son_ex2
let son_ex2 = new Son()
son_ex2.addCars('mashaladi')
console.log(son_ex1,son_ex2)
Copy the code
As a result, the cars attribute of instance SON_ex2 changes, as does the cars attribute of instance SON_ex1.
(2) borrow constructor inheritance
How it works: Calls the superclass constructor in the subclass constructor
The parent class:
function Dad(name,age){
this.name = name
this.age = age
this.cars = ['baoma'.'liebao']
this.addCars = function(car){
this.cars.push(car)
}
}
Dad.prototype.getCar = function(id){
return this.cars[id]
}
Copy the code
Call the parent constructor from a subclass constructor:
function Son(name,age){
Dad.call(this,name,age)// Call the Dad function, change the this pointer, and execute the function.
}
Copy the code
New two instances of subclasses, pass in name,age, etc., and add son_ex2’s car.
let son_ex1 = new Son('son1'.10)
let son_ex2 = new Son('son2'.20)
son_ex2.addCars('mashaladi')
console.log(son_ex1,son_ex2)
Copy the code
- Subclass instances inherit properties and methods from the parent class constructor.
Parent class attributes name,age, method addCars, subclass instances.
- The cars property of the parent class is not shared by all instances and does not affect each subclass instance.
Add the car type of instance son_ex2, instance son_ex1 is not affected.
Each subclass instance has a copy of the parent class instance function, and no function reuse is implemented.
- You can pass arguments to a superclass function
The name and age attributes can also be passed in.
- Unable to inherit methods and properties from superclass stereotypes
At the same time, we find that the method dad.prototype. getCar on the parent prototype is missing.
son_ex1.getCar(0) //son_ex1.getCar is not a function
Copy the code
- Multiple parent class inheritance can be implemented
function Dad1(price){
this.price = price
}
function Dad2(phone){
this.phone = phone
}
function Son(name,age,price,phone){
Dad.call(this,name,age)// Call the Dad function, change the this pointer, and execute the function.
Dad1.call(this,price)
Dad2.call(this,phone)
}
Copy the code
(3) combinatorial inheritance -> prototype chain + borrow constructor
Principle: borrow the above two ways, constructor + prototype chain inheritance: retain the pass parameter, realize reuse
The parent class:
function Dad(name,age){
this.name = name
this.age = age
this.cars = ['baoma'.'liebao']
this.addCars = function(car){
this.cars.push(car)
}
}
Dad.prototype.getCar = function(id){
return this.cars[id]
}
Copy the code
In a subclass, you need to point the stereotype to the parent instance and point the constructor of the stereotype to yourself.
function Son(name,age){
Dad.call(this,name,age)// Call the Dad function, change the this pointer, and execute the function
}
Son.prototype = new Dad()
Son.prototype.constructor = Son
Copy the code
To create instances of two subclasses, add the appropriate parameters, and add the car type to one of the instances
let son_ex1 = new Son('son1'.10)
let son_ex2 = new Son('son2'.20)
son_ex2.addCars('mashaladi')
console.log(son_ex1,son_ex2)
Copy the code
-
Passable, reusable, and can inherit properties and methods from the parent class prototype
-
The parent class is called twice, generating two instances
Call (this,name,age) is called once, and new Dad() is called once.
(4) Inheritance of the original type
The original type inherits version 1
How it works: Copy an object, use it as a prototype, and return a temporary instance
In layman’s terms, it simulates copying an object.
Let’s first define an object, the parent object
let Dad = {
name:' '.age:' '.cars: ['baoma'.'laoteleisi'].addCar:function(car){
this.cars.push(car)
}
}
Copy the code
Defines a function that points its prototype to the parent object and returns a new instance
function create(obj){
function Dad(){}
Dad.prototype = obj
return new Dad()
}
Copy the code
To create two subclass instances, change the name of the subclass instance and add a car to son_ex2.
let son_ex1 = create(Dad)
let son_ex2 = create(Dad)
son_ex1.name = 'son_ex1'
son_ex2.name = 'son_ex2'
son_ex2.addCar('falali')
console.log(son_ex1,son_ex2)
Copy the code
It has the same disadvantages as prototype chain inheritance.
- Attributes of the parent object reference type that are shared by subclass instances
Cars is a reference type, and instance son_ex2 has changed its value, as has instance son_ex1
- Unable to reuse
Instance son_ex1 son_ex2 added the name attribute later.
Original type inherited version 2
In ES5, an object.create () is added to standardize the original type inheritance
Object.create(prototype,desc)
Copy the code
prototype
Object used as a prototype, can be null, required.desc
Optional arguments, JavaScript objects that contain one or more property descriptors, simply define additional properties for the new object, and any properties specified override the properties of the same name on the prototype.
Let’s define a parent object
let Dad = {
name:' '.age:' '.cars: ['baoma'.'laoteleisi'].addCar:function(car){
this.cars.push(car)
}
}
Copy the code
Instantiate subclasses, change the names of both subclass instances, and add a car to son_ex2
let son_ex1 = Object.create(Dad)
let son_ex2 = Object.create(Dad)
son_ex1.name = 'son_ex1'
son_ex2.name = 'son_ex2'
son_ex2.addCar('falali')
console.log(son_ex1,son_ex2)
Copy the code
The same as the previous version of the original type inheritance.
Let’s use the second argument to Object.create, which adds new attributes and overrides old ones.
let son_ex1 = Object.create(Dad,{
name: {value:'son_ex1'
},
cars: {value: ['baoma']}})let son_ex2 = Object.create(Dad,{
name: {value:'son_ex2'
},
cars: {value: ['mashaladi']
}
})
son_ex2.addCar('falali')
console.log(son_ex1,son_ex2)
Copy the code
Because the attributes are added for subclass instances, the instances do not affect each other.
The disadvantages are the same as above. …
(5) Parasitic inheritance
Parasitic inheritance and prototype inheritance are similar ideas
How it works: Create a function that encapsulates the inheritance process, and then do some internal enhancements to return a new object
Let’s define a parent object
let Dad = {
name:' '.age:' '.cars: ['baoma'.'laoteleisi'].addCar:function(car){
this.cars.push(car)
}
}
Copy the code
Defines a function that points its prototype to the parent object and returns a new instance
function object(obj){
function Dad(){}
Dad.prototype = obj
return new Dad()
}
Copy the code
Let’s define a function that creates an object
function createSon(obj){
let son = object(obj)
son.addAge = function(age){
this.age = age
}
return son
}
Copy the code
Let’s create two subclasses, change the name, age, and add the car.
let son_ex1 = createSon(Dad)
let son_ex2 = createSon(Dad)
son_ex1.name = 'son_ex1'
son_ex2.name = 'son_ex2'
son_ex1.addAge(10)
son_ex2.addAge(20)
son_ex2.addCar('falali')
console.log(son_ex1,son_ex2)
Copy the code
-
Parent object properties are shared by all instances
-
Can’t reuse
The difference between the parasitic inheritance and the original type is that the parasitic inheritance encapsulates the original type inheritance, and the principle disadvantages and advantages are the same.
(6) Parasitic combination inheritance -> parasitic + combination
Principle: The combination of parasitic inheritance and composite inheritance solves the problem that the parent constructor is called twice in composite inheritance.
Defines a function that points its prototype to the parent object and returns a new instance
function object(obj){
function F(){}
F.prototype = obj
return new F()
}
Copy the code
The parent class:
function Dad(name,age){
this.name = name
this.age = age
this.cars = ['baoma'.'liebao']
this.addCars = function(car){
this.cars.push(car)
}
}
Dad.prototype.getCar = function(id){
return this.cars[id]
}
Copy the code
Combinatorial inheritance: Inherits methods and properties from the parent class through constructors, and inherits methods and properties from the parent class prototype through stereotypes.
Parasitic combinatorial: inherits methods and properties from the parent class through constructors, and inherits methods and properties from the parent class prototype through primitives.
In short, we no longer need to point the subclass prototype to the parent instance, so we no longer need to new a parent instance. All we need is a copy of the parent stereotype.
We define an inherited model.
function inheritPrototype(Son,Dad){
let prototype = object(Dad.prototype) // Create an object
prototype.constructor = Son
Son.prototype = prototype
}
Copy the code
Define a subclass and have it inherit from its parent class stereotype
function Son(name,age){
Dad.call(this,name,age)
}
inheritPrototype(Son,Dad)
Copy the code
To test this, create two instances of subclasses and modify their name attribute to add cars.
let son_ex1 = new Son('son_ex1'.10)
let son_ex2 = new Son('son_ex2'.20)
son_ex1.addCars('mashaladi')
son_ex2.addCars('laoteleisi')
console.log(son_ex1,son_ex2)
Copy the code
- The parent class constructor is called only once
- Method attributes can be reused and parameters can be passed
- Superclass attributes are not shared by all subclass instances
- Properties and methods in the superclass constructor are inheritable, as are properties and methods on the stereotype
- Can inherit from multiple parent classes
(7) ES6 class inheritance
In ES6, calSS was introduced as a template for objects that can define a class through the class keyword.
But the essence of class is still function.
The class keyword is just the syntactic sugar of the prototype, with parasitic combinatorial inheritance at the bottom
To learn more about class, check out my other article [New features of ES6]
Let’s define a parent class
class Dad{
// constructor
constructor(name,age){
this.name = name
this.age = age
this.cars = ['baoma'.'liebao']}// General method
addCars(car){
this.cars.push(car)
}
}
Copy the code
Define a subclass that extends from the parent class through the extends keyword
class Son extends Dad{
constructor(name,age){
super(name,age)// Call the parent constructor through super}}Copy the code
Let’s create two subclasses and modify the car in instance 2
let son_ex1 = new Son('son_ex1'.10)
let son_ex2 = new Son('son_ex2'.20)
son_ex2.addCars('mashaladi')
console.log(son_ex1,son_ex2)
Copy the code
Note, however, that not all browsers support the class keyword.
conclusion
There are seven main methods of inheritance:
- Prototype chain inheritance
- Borrow constructor inheritance
- Combinatorial inheritance
- Primary inheritance
- Parasitic inheritance
- Parasitic combinatorial inheritance
- ES6class inheritance