Inheritance is a fundamental concept of object-oriented languages, and OO languages generally support two types of inheritance: interface inheritance and implementation inheritance. Interface inheritance inherits only method signatures, whereas implementation inheritance inherits the actual methods. Functions in ECMAScript do not have signatures, so interface inheritance cannot be implemented. ECMAScript only supports implementation inheritance, which is largely implemented by prototype chains.

Prototype chain

As the main method of inheritance, the basic idea of stereotype chain is to make one reference type inherit the attributes and methods of another reference type by using stereotypes.

The simple code for inheritance using the idea of prototype chain is as follows:

function Parent() {
    this.name = 'zhangsan';
    this.children = ['A'.'B'];
}

Parent.prototype.getChildren = function() {
    console.log(this.children);
}

function Child() {

}

Child.prototype = new Parent();

var child1 = new Child();
child1.children.push('child1')
console.log(child1.getChildren()); // Array ["A", "B", "child1"]

var child2 = new Child();
child2.children.push('child2')
console.log(child2.getChildren()); // Array ["A", "B", "child1", "child2"]
Copy the code

Inheritance is implemented by pointing the stereotype of a subclass to an instance of the parent class, noting that the subclass’s constructor points to the parent class

The main problem with stereotype chain inheritance is that attributes of reference types are shared by all instances and cannot be passed to Parent when creating an instance of Child

Borrow constructors

To solve the problem of including reference type values in stereotypes, people began to implement inheritance using a technique called borrowing constructors. The basic idea of this technique is very simple: call the superclass constructor inside the subclass constructor

function Parent(age) {
    this.names = ['lucy'.'dom'];
    this.age = age;

    this.getName = function() {
        return this.name;
    }

    this.getAge = function() {
        return this.age; }}function Child(age) {
    Parent.call(this, age);
}

var child1 = new Child(18);
child1.names.push('child1');
console.log(child1.names); // [ 'lucy', 'dom', 'child1' ]

var child2 = new Child(20);
child2.names.push('child2');
console.log(child2.names); // [ 'lucy', 'dom', 'child2' ]
Copy the code

Disadvantages: Methods are defined in constructors, so every time an instance is created the method is created, so function reuse is out of the question

3. Combinatorial inheritance

Combined inheritance is an inheritance mode that combines the prototype chain and the borrowed constructor technology, and gives full play to the advantages of both. The idea behind it is to use the prototype chain to inherit the prototype attributes and methods, and use the borrowed constructor to inherit the instance attributes. This ensures that functions can be reused through the prototype-defined methods and that each instance has its own attributes.

function Parent(name, age) {
    this.name = name;
    this.age = age;
    this.colors = ['red'.'green']
    console.log('parent')
}

Parent.prototype.getColors = function() {
    console.log(this.colors);
}

function Child(name, age, grade) {
    Parent.call(this, name, age);// It is executed once when subclass instances are created
    this.grade = grade;
}

Child.prototype = new Parent(); // Specify that the subclass prototype is executed once
Child.prototype.constructor = Child;// Calibrate the constructor
Child.prototype.getName = function() {
    console.log(this.name)
}

var c = new Child('alice'.10.4)
console.log(c.getName())

> "parent"
> "parent"
> "alice"
Copy the code

Combination inheritance inheritance has both the prototype chain can reuse function characteristics, and have to borrow the constructor method can ensure each subclass instance can have their own properties, and to participate the superclass feature, but the combination of inheritance is not perfect implementation inheritance way, because in this way to create a subclass will call the superclass constructor twice.

Iv. Inheritance of the original type

Instead of using a strict constructor, the idea is that stereotypes allow you to create new objects based on existing objects without having to create custom types.

function object(o) {
    function F(){};
    F.prototype = o;
    return new F();
}
Copy the code

Inside the object() function, a temporary constructor is created, the passed object is used as a prototype of the constructor, and a new instance of the temporary type is returned. Essentially, object() completes a shallow copy

var person = {
    name: 'alice'.friends: ['leyla'.'court'.'van']}var p1 = object(person);
p1.name = 'p1';
p1.friends.push('p1');

var p2 = object(person);
p2.name = 'p2';
p2.friends.push('p2');

console.log(p1.name)
console.log(person.friends) 

> Array ["leyla"."court"."van"."p1"."p2"]
Copy the code

ECMAScript5 normalizes old-style inheritance with the addition of the object.create () method, which takes two parameters: an Object to be used as a prototype for the new Object and an Object to define properties for the new Object

var person = {
    name: 'alice'.friends: ['leyla'.'court'.'van']}var p1 = Object.create(person);
p1.name = 'p1';
p1.friends.push('p1');

var p2 = Object.create(person);
p2.name = 'p2';
p2.friends.push('p2');

console.log(p1.name)
console.log(person.friends) 

> Array ["leyla"."court"."van"."p1"."p2"]
Copy the code

Parasitic inheritance

Parasitic inheritance is closely related to prototype inheritance, where you create a function that simply encapsulates the process of an inherited function, enhances the object internally in some way, and returns the object.

function object(obj) {
    function F(){};
    F.prototype = obj;
    return new F();
}

function createAnother(original) {
    var clone = object(original); // Create a new object
    clone.sayHi = function(){ 
        console.log('hello, world'); // Enhance the object by adding attributes or methods
    }
    return clone; // Return a new object
}

var person = {
    name: 'alice'.friends: ['Sherly'.'Taissy'.'Vant']}var p1 = createAnother(person);
p1.sayHi(); 

> "hello, world"
Copy the code

The new object not only has the attributes and methods of the Person object, but also has its own sayHi() method flaw: using parasitic inheritance to add functions to an object is inefficient because of the inability to reuse functions, similar to the constructor pattern

Parasitic combinatorial inheritance

The biggest problem with composite inheritance, the most common inheritance pattern in JavaScript, is that the superclass constructor is called twice in any case: once when the subclass stereotype is created and once inside the subtype constructor. Subtypes eventually contain all of the instance attributes of the superclass. The basic idea behind parasitic combinatorial inheritance, which inherits properties through constructors and methods through stereotype chains, is that you don’t need to call the constructor of a superclass to specify the stereotype of a subclass, all you need is a copy of the superclass stereotype. The basic pattern of parasitic combination inheritance is as follows:

// Copy the parent's prototype object
function create(original) {
    function F(){};
    F.prototype = original;
    return new F();
}

// Create a prototype copy of the parent class, change the prototype of the subclass, and correct the constructor
function inherit(subClass, superClass) {
    var parent = create(superClass.prototype);
    parent.constructor = subClass;
    subClass.prototype = parent;
}
Copy the code

The inherit() function implements the simplest form of parasitic combinatorial inheritance, taking two arguments: the subclass constructor and the parent class constructor. Three steps take place inside the function: the first step is to create a copy of the superclass stereotype, the second step is to add constructor properties to the created copy to compensate for the default constructor properties lost by rewriting the stereotype, and finally, to assign the newly created object to the stereotype of the subclass. Therefore, we can use this function to replace the statement that assigns a value to a subclass’s prototype in combinatorial inheritance, i.e. :

Child.prototype = new Parent();
Copy the code

A complete inheritance example:

function Parent(name, age){
    this.name = name;
    this.age = age;
    console.log('parent')
}

Parent.prototype.getName = function(){
    return this.name;
}

function Child(name, age, grade){
    Parent.call(this, name, age);
    this.grade = grade;
}

// The parasitic combination
// Copy the parent's prototype object
function create(original) {
    function F(){};
    F.prototype = original;
    return new F();
}

// Create a prototype copy of the parent class, change the prototype of the subclass, and correct the constructor
function inherit(subClass, superClass) {
    var parent = create(superClass.prototype);
    parent.constructor = subClass;
    subClass.prototype = parent;
}

inherit(Child, Parent);

var child = new Child('lucy'.12.5);

> "parent"
Copy the code

The high efficiency of parasitic composite inheritance is that it only calls the superclass constructor once, while keeping the prototype chain unchanged. It can normally use Instanceof and isPrototypeOf(). Parasitic composite inheritance is generally considered to be the most ideal inheritance method for reference types

Enhanced parasitic combination inheritance

Parasitic combinatorial inheritance can achieve inheritance perfectly, but it is not without disadvantages. The inherit() method copies the stereotype of the parent class and assigns it to the subclass. If the subclass stereotype has custom methods, it will also be overwritten. Therefore, you can add attributes or methods defined by the subclass stereotype to the copied stereotype Object using object.defineProperty. It can preserve the integrity of the prototype object of the subclass and copy the parent prototype.

function Parent(name, age){
    this.name = name;
    this.age = age;
}

Parent.prototype.getName = function(){
    console.log(this.name)
}

function Child(name, age, grade){
    Parent.call(this, name, age);
    this.grade = grade;
}

// Child.prototype = new Parent();

// function inherit(child, parent){
// let obj = parent.prototype;
// obj.constructor = child;
// child.prototype = obj;
// }

function inherit(child, parent){
    let obj = parent.prototype;
    obj.constructor = child;
    for(let key in child.prototype){
        Object.defineProperty(obj, key, {
            value: child.prototype[key]
        })
    }
    child.prototype = obj;
}
Copy the code

Several ways to compare:

Inheritance way advantages defects
Prototype chain inheritance Function reuse can be realized 1. Attributes of reference type are shared by all instances; 2. Do not pass parameters to a superclass when creating a subclass
Borrowing constructor 1. Avoid reference type attributes being shared by all instances; 2. You can pass arguments to superclasses in subclasses Methods are defined in constructors and are created every time an instance is created, making function reuse impossible
Combination of inheritance Combining the advantages of prototype chain inheritance and constructors, it is the most commonly used inheritance pattern in Javascript Creating a subclass calls the superclass’s constructor twice
Prototype inheritance In cases where there is no need to create constructors and you just want one object to be similar to another, old-style inheritance is perfectly fine Properties of reference types are shared by all instances
Parasitic inheritance You can enhance objects Similar to the constructor pattern, using parasitic inheritance to add functions to objects is inefficient because functions cannot be reused. There is also a defect that attributes of reference type are shared by all instances
Parasitic combinatorial inheritance A copy of the superclass stereotype is copied without having to call the superclass constructor; Functions can be reused and referenced type instances cannot be shared by subclasses, while creating subclasses requires only one call to the superclass constructor