Six ways of JS inheritance

Keywords: Java Javascript html5

Interface inheritance cannot be implemented in ECMAScript because the function is not signed. ECMAScript only supports inheritance, and its main way to realize inheritance depends on prototype chain.

Prototype chain inheritance         

Relationship between constructor, prototype chain and instance: each constructor has a prototype object, each prototype object contains a pointer to the constructor, and each instance contains an internal pointer to the prototype object (_proto_).

All reference types inherit Object by default. This inheritance is also realized through the prototype chain.

Important: make the prototype of the child class equal to the instance of the parent class.

be careful:

  • Subtypes sometimes need to override supertypes or add supertype methods. The code for adding methods to the prototype must be placed after replacing the prototype.
  • When creating a subclass instance, you cannot pass parameters to the parent class.
  • All new instances share the properties of the parent instance. (the properties on the prototype are shared. If one instance modifies the prototype properties, the prototype properties of another instance will also be modified!)
  • The constructor of the prototype of the subclass will point to the constructor of the prototype (_proto_) of the new prototype of the subclass (instance of the parent class).
// Parent class
function Person(name) {
    this.name = name;
    this.sum = function() {
        console.log(this.name)
    }
}
Person.prototype.age = 18;

// Subclass
function Child() {
    this.name = 'kid'
}

Child.prototype = new Person(); // Prototype chain inheritance focus

Borrowing constructor inheritance

Important: call the parent constructor inside the subclass constructor, and use the call() and apply() methods to introduce the parent constructor into the subclass constructor.

advantage:

  • It solves the shortcomings of prototype chain inheritance
  • You can inherit multiple constructors (call / apply).
  • You can pass parameters to the parent class.

Disadvantages:

  • Only the properties of the parent class constructor can be inherited, and the properties of the parent class prototype are not inherited.
  • Cannot implement reuse of constructors.
// Parent class
function Person(name) {
    this.name = name;
    this.sum = function() {
        console.log(this.name)
    }
}
Person.prototype.age = 18;

// Subclass
function Child() {
    Person.call(this, 'hhhqzh'); // Borrowing constructor inheritance focus
 
    this.name = 'kid';
}


Composite inheritance (pseudo classical inheritance; prototype chain + borrowing constructor) common

Combine the prototype chain foundation with borrowing constructor inheritance.

The prototype chain is used to inherit the prototype properties and methods of the parent class, and the constructor is used to inherit the instance properties. Guarantee not only realizes the reuse of functions by defining methods on the prototype, but also ensures that each instance has its own attributes.

Key point: combining advantages of the two inheritance modes, transfer participation and reuse.

advantage:

  • You can inherit the properties and methods on the parent class prototype, and you can pass parameters and reuse the parent class constructor.
  • The constructor properties introduced by each subclass instance are private and not shared like prototype chain inheritance.

Disadvantages:

  • Call the parent constructor twice (memory consumption).
  • The constructor of the prototype of the subclass will point to the constructor of the prototype (_proto_) of the new prototype of the subclass (instance of the parent class).
// Parent class
function Person(name) {
    this.name = name;
    this.sum = function() {
        console.log(this.name)
    }
}
Person.prototype.age = 18;

// Subclass
function Child(name) {
    Person.call(this, name); // Borrowing constructor inheritance focus
}


Child.prototype = new Person(); // Prototype chain inheritance focus

let child = new Child('hhhqzh');

console.log(child.name); // hhhqzh inherits the properties of the parent constructor
console.log(child.age); // 18 inherit the properties of the parent class prototype

Prototype inheritance

Prototypes allow you to create new objects based on existing objects without having to create custom types.

Important: you must have an object as the basis of another object, wrap an object with a function, and then return a new object.

Features: similar to copying an object, wrapped with functions. Object.create takes advantage of this principle.

Disadvantages:

  • All instances inherit properties on the prototype.
  • Reuse cannot be achieved. (the properties of the new instance are added after)
// Parent class
function Person(name) {
    this.name = name;
    this.sum = function() {
        console.log(this.name)
    }
}
Person.prototype.age = 18;

// First encapsulate a function container, create a temporary constructor inside the function, then take the incoming object as the prototype of the constructor, and finally return an instance of the constructor.
function object(obj) {
    function F(){};
    F.prototype = obj;
    return new F();
}

let person = new Person('hhhqzh');
let child = object(person);
console.log(child.name); // hhhqzh inherits the properties of the parent function

Parasitic inheritance

Create a function that is only used to encapsulate the integration process, enhance the object within the function (any function that can return a new object, such as prototype inheritance), and finally return the object.

Disadvantages: no prototype is used and reuse cannot be realized.

// Parent class
function Person(name) {
    this.name = name;
    this.sum = function() {
        console.log(this.name)
    }
}
Person.prototype.age = 18;

// First encapsulate a function container, create a temporary constructor inside the function, then take the incoming object as the prototype of the constructor, and finally return an instance of the constructor.
function object(obj) {
    function F();
    F.prototype = obj;
    return new F();
}

// Create a function that encapsulates the inheritance process, which internally enhances the object in some way.
function creeateAnother(original){
    let clone = object(original); // Inheritance process
    clone.name = 'ppptyp';
    clone.sayHi = function() {
        console.log("hi");    
    }
    return clone;
}

let person = new Person('hhhqzh');
let child = creeateAnother(person);
child.sayHi(); // hi
console.log(child.name); // ppptyp 

Parasitic combinatorial inheritance (common)

That is, it inherits attributes by borrowing constructors, and inherits methods through the hybrid form of prototype chain. In essence, parasitic inheritance is used to inherit the prototype of the parent type (the prototype of the parent class is equal to an instance), and then assign this instance to the prototype of the child type.

Advantage: fixed the problem of composite inheritance (calling constructor twice)

// Parent class
function Person(name) {
    this.name = name;
    this.sum = function() {
        console.log(this.name)
    }
}
Person.prototype.age = 18;

// Subclass
function Child(name) {
    Person.call(this, name); // Borrowing constructor inheritance
}

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

// parasitic
function creeateAnother(Child, Person){
    let prototype = object(Person.prototype); // Inheritance process
    prototype.constructor = Child;
    Child.prototype = prototype;
}

// Child.prototype = new Person(); //  Prototype chain inheritance
creeateAnother(Child, Person); // Replace prototype chain inheritance

let child = new Child('hhhqzh');

console.log(child.name); // hhhqzh inherits the properties of the parent constructor
console.log(child.age); // 18 inherit the properties of the parent class prototype

Posted by sergio-pro on Fri, 15 Oct 2021 11:17:02 -0700