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
  • prototypeObject used as a prototype, can be null, required.
  • descOptional 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