Javascript inheritance summary

Posted by beboni on Thu, 09 Dec 2021 00:30:21 +0100

People who are used to writing TypeScript can easily understand extensions, such as class inheritance and interface inheritance. Portal: www.tslang.cn/docs/handbo... But how did JavaScript implement inheritance before ES2015? There is no doubt that inheritance can only be realized through prototype chain. This article mainly encountered the problem of prototype inheritance when reading. It reviews the following centralized methods of prototype inheritance and arranges them into notes for future reference.

Class inheritance (prototype chain inheritance)

Class inheritance is very simple. To sum up, the prototype of the subclass is given to the instance of the parent class, so as to inherit the properties and methods of the parent class.

// Define the Person class
function Person() {
    this.name = 'zhang san'
}
// Add member function
Person.prototype.getName = function() {
    console.log(this.name)
}
// Define the Employee class
function Employee() {
    
}
// Class inheritance
Employee.prototype = new Person()
let employee1 = new Employee()
console.log(employee1.getName()) // zhang san
 Copy code

Moreover, you can judge that the subclass instance is the parent class prototype through the instanceof keyword:

let employee1 = new Employee()
console.log(employee1 instanceof Employee) // true
console.log(employee1 instanceof Person) // true
console.log(employee1 instanceof Object) // true, the top-level prototype is Object
 Copy code

However, there are often many problems with class inheritance:

  1. The attribute of reference type is shared by all instances;
// Define the Person class
function Person() {
    this.name = 'zhang san'
    this.skill = ['having dinner', 'sleep']
}
// Add member function
Person.prototype.getSkill = function() {
    console.log(this.skill)
}
// Define the Employee class
function Employee() {
    
}
// Class inheritance
Employee.prototype = new Person()

let employee1 = new Employee()
employee1.skill.push('Write code')

let employee2 = new Employee()
employee2.skill.push('Make decisions')

console.log(employee1.getSkill()) // ['eat', 'sleep', 'write code', 'make decisions']
console.log(employee2.getSkill()) // ['eat', 'sleep', 'write code', 'make decisions']
Copy code
  1. When creating an Employee instance, you cannot pass parameters to Person.

Constructor inheritance

Although class inheritance is simple, its disadvantages are very obvious. Can we avoid these disadvantages through other inheritance methods? In fact, constructor inheritance can solve this problem. In summary, constructor inheritance is: the subclass constructor environment executes the parent constructor once.

// Define the Person class
function Person() {
    this.name = 'zhang san'
    this.skill = ['having dinner', 'sleep']
}
// Add member function
Person.prototype.getSkill = function() {
    console.log(this.skill)
}
// Define the Employee class and inherit the Person class
function Employee() {
    Person.call(this)
}

let employee1 = new Employee()
employee1.skill.push('Write code')

let employee2 = new Employee()
employee2.skill.push('Make decisions')

console.log(employee1.skill) // ['eat', 'sleep', 'write code']
console.log(employee2.skill) // ['eat', 'sleep', 'make decisions']

// Define the Manager class and inherit the Person class
function Manager(skills) {
    Person.call(this, skills)
}

let manager1 = new Manager(['having dinner', 'sleep', 'Make decisions'])
console.log(manager1.skill) // ['eat', 'sleep', 'make decisions']

console.log(manager1.getSkill()) // An error is reported. There is no such method
 Copy code

It can be seen that constructor inheritance has perfectly solved the two problems of class inheritance, but there is a new problem, that is, constructor inheritance only inherits the properties of the parent class, and the methods in the parent class will not be inherited by the subclasses.

Combinatorial inheritance

At first glance, there are more or less problems with the above two methods, which requires composite inheritance, which is also the most common inheritance method in JavaScript. In a word, class inheritance + constructor inheritance.

// Define the Person class
function Person() {
    this.name = 'zhang san'
    this.skill = ['having dinner', 'sleep']
}
// Add member function
Person.prototype.getSkill = function() {
    console.log(this.skill)
}
Person.prototype.getName = function() {
    console.log(this.name)
}
// Define the Employee class and inherit the Person class
function Employee(name, skill) {
    Person.call(this, name, skill)
}
Employee.prototype = new Person()

let employee1 = new Employee('zhang san', ['having dinner', 'sleep', 'Write code'])
console.log(employee1.getName()) // zhang san
console.log(employee1.getSkill()) // ['eat', 'sleep', 'write code']

let employee2 = new Employee('li si', ['having dinner', 'sleep', 'Make decisions'])
console.log(employee2.getName()) // li si
console.log(employee2.getSkill()) // ['eat', 'sleep', 'make decisions']
Copy code

Combined inheritance integrates two inheritance methods, which solves the problems caused by the previous two methods. But in fact, the parent constructor is called twice in one inheritance, which seems very puzzling:

// Set the prototype of the subtype instance and call the parent class constructor
Employee.prototype = new Person()

// Create a subclass instance and call the parent class constructor
Person.call(this, name, skill)
Copy code

Prototype inheritance

Someone has proposed a new inheritance method: prototype inheritance. In summary, it is to create a new object according to the existing object without creating a new user-defined object type. In short, prototype inheritance encapsulates the following create function:

function create(o) {
    function F(){}
    f.prototype = o
    return new F()
}
Copy code

Friends familiar with the ES specification can see that this is the simulated implementation of Object.create(). The disadvantage is still obvious. The value type attribute is copied and the reference type attribute is shared, which is similar to the problem of prototype chain inheritance:

// Define the Person class
function Person() {
    this.name = 'zhang san'
    this.skill = ['having dinner', 'sleep']
}

let person1 = create(Person)
let person2 = create(Person)

person1.name = 'li si'
console.log(person1.name) // li si

person1.skill = ['having dinner', 'sleep', 'Write code']
console.log(person2.skill) // ['eat', 'sleep', 'write code']
Copy code

Does this mean that prototype assignment is useless? You can look down.

Parasitic inheritance

The parasitic inheritance method is the same as the name. In summary, it is to create a function that is only used to seal the inheritance process. The function internally enhances the object in some form, and finally returns the object.

function create(o) {
    let clone = Object.create(o)
    clone.getName = function() {
        console.log('get name')
    }
    return clone
}
Copy code

Of course, like constructor inheritance, it does not inherit the parent method. You need to create a method every time you create an object.

Parasitic combinatorial inheritance

Parasitic composite inheritance is actually parasitic inheritance + class inheritance. The purpose is to solve the problem of calling the parent class construction method twice when creating an object by composite inheritance. If you don't use Employee.prototype = new Person(), but indirectly let Employee.prototype access Person.prototype?

function create(o) {
    function F() {}
    F. prototype = o
    return new F ()
}
function prototypr(child, parent) {
    let prototype = object(parent.prototype)
    prototype.constructor = child
    child.prototype = prototype
}

prototypr(Employee, Person)
Copy code

Parasitic combination solves the problem of calling the parent constructor twice, which can be regarded as the ultimate solution of JavaScript.

summary

This paper summarizes class inheritance, constructor inheritance, combinatorial inheritance, prototype inheritance, parasitic inheritance and parasitic combinatorial inheritance. It is recommended that students who do not know much about prototype chain and inheritance take a closer look. For ES6 class inheritance, please move to ES6 introductory tutorial es6.ruanyifeng.com/#docs/class...

reference resources

  1. JavaScript Design Patterns
  2. JavaScript advanced programming (Second Edition)