6

This article is more appropriately called a note, and the content comes from "Advanced JavaScript Programming (Third Edition)" Chapter 6.3 Inheritance

Several inheritance methods in JavaScript

  1. Prototype chain inheritance
  2. Using Constructor Inheritance (Classic Inheritance)
  3. Combinatorial inheritance: prototype chain + borrowed constructor (most common)
  4. Prototype inheritance (Object.create)
  5. parasitic inheritance
  6. Parasitic composition inheritance (ideal)

    1. Inheritance in ES6

1. Prototype chain inheritance

The prototype of the subtype is an instance object of the supertype

 function Parent() {
    this.name = 'bigStar';
    this.colors = ['red', 'blue', 'yellow'];
}
Parent.prototype.getName = function() {
    console.log(this.name)
}

function Child() {
    this.subName = 'litterStar';
}
// 核心代码: 子类型的原型为父类型的一个实例对象
Child.prototype = new Parent();

let child1 = new Child();
let child2 = new Child();
child1.getName(); // bigStar


child1.colors.push('pink');
// 修改 child1.colors 会影响 child2.colors
console.log(child1.colors); // [ 'red', 'blue', 'yellow', 'pink' ]
console.log(child2.colors); // [ 'red', 'blue', 'yellow', 'pink' ]
Note the core code: Child.prototype = new Parent();

Features:

  1. The method added to the constructor of the parent class can be accessed by the subclass

shortcoming:

  1. All properties from the prototype object are shared by all instances, child1 modifying the colors will affect the colors of child2
  2. When creating an instance of a subclass, you cannot pass parameters to the constructor of the parent class

2. With the help of constructor inheritance (classical inheritance)

Use call() or apply() in the subclass's constructor to call the supertype constructor

 function Parent(name) {
    this.name = name;
    this.colors = ['red', 'blue', 'yellow'];
}
Parent.prototype.getName = function() {
    console.log(this.name)
}

function Child(name, age) {
    // 核心代码:“借调”父类型的构造函数
    Parent.call(this, name);
    this.age = age;
}

let child1 = new Child('litterStar');
let child2 = new Child('luckyStar');
console.log(child1.name); // litterStar
console.log(child2.name); // luckyStar

// 这种方式只是实现部分的继承,如果父类的原型还有方法和属性,子类是拿不到这些方法和属性的。
child1.getName(); // TypeError: child1.getName is not a function
Note the core code: Parent.call(this, name);

Features:

  • Avoid properties of reference types being shared by all instances
  • When creating an instance of a subclass, you can pass parameters to the parent class

shortcoming

  • An instance is not an instance of the parent class, just an instance of the child class
  • Can only inherit the instance properties and methods of the parent class, not the prototype properties and methods
  • Function reuse cannot be achieved, and methods are created every time an instance is created, which affects performance

3. Combination inheritance: prototype chain + borrowed constructor (most commonly used)

 function Parent(name) {
    this.name = name;
    this.colors = ['red', 'blue', 'yellow'];
}
Parent.prototype.getName = function() {
    console.log(this.name)
}

function Child(name, age) {
    // 核心代码①
    Parent.call(this, name);

    this.age = age;
}
// 核心代码②: 子类型的原型为父类型的一个实例对象
Child.prototype = new Parent();
Child.prototype.constructor = Child;


// 可以通过子类给父类的构造函数传参
let child1 = new Child('litterStar');
let child2 = new Child('luckyStar');
child1.getName(); // litterStar
child2.getName(); // luckyStar

child1.colors.push('pink');
// 修改 child1.colors 不会影响 child2.colors
console.log(child1.colors); // [ 'red', 'blue', 'yellow', 'pink' ]
console.log(child2.colors); // [ 'red', 'blue', 'yellow' ]
Note the core code: Parent.call(this, name); and Child.prototype = new Parent();

Features

  • It combines the advantages of prototype chain inheritance and borrowing constructors, and is called the most commonly used inheritance pattern in JavaScript.

    shortcoming

  • The parent class constructor is called twice, and two instances are generated

    • Once is when setting the prototype of the subtype instance Child.prototype = new Parent();
    • Once when a subtype instance is created let child1 = new Child('litterStar'); , calling new will execute Parent.call(this, name); , at this time it will call again Parent constructor

4. Prototype inheritance (Object.create)

With the help of prototypes, objects can be created based on existing methods. var B = Object.create(A) takes the A object as the prototype to generate the A object, and B inherits all the properties and methods of A.

 const person = {
    name: 'star',
    colors: ['red', 'blue'],
}

// 核心代码:Object.create
const person1 = Object.create(person);
const person2= Object.create(person);

person1.name = 'litterstar';
person2.name = 'luckystar';

person1.colors.push('yellow');

console.log(person1.colors); // [ 'red', 'blue', 'yellow' ]
console.log(person2.colors); // [ 'red', 'blue', 'yellow' ]
Note the core code: const person1 = Object.create(person);

Features

  • There is no constructor in the strict sense, with the help of prototypes, new objects can be created based on existing objects

    shortcoming

  • All properties from the prototype object are shared by all instances, and the modification of colors by person1 will affect the colors of person2, which is the same as prototype chain inheritance.

5. Parasitic inheritance

Create a function that encapsulates the inheritance process that internally enhances the object in some way

 function createObj (original) {
    // 通过调用函数创新一个新对象
    var clone = Object.create(original);
    // 以某种方式来增强这个对象
    clone.sayName = function () {
        console.log('hi');
    }
    // 返回这个对象
    return clone;
}

Disadvantage: Every time you create an object, you will create a method, just like using the constructor pattern

6. Parasitic composition inheritance (ideal)

We can first recall the most commonly used inheritance pattern in JavaScript: Combined inheritance (prototype chain + borrowed constructor), its biggest disadvantage is that it will call the parent constructor twice ( Child.prototype = new Parent(); and let child1 = new Child('litterStar'); ).

Can we think of a way to call it once? Parent.prototype can be accessed from Child.prototype.

We can't use Child.prototype = Parent.prototype directly, because there will be some side effects, you may modify Child.prototype when you modify Parent.prototype .

You can use Object.create(...) to achieve

Object.create Explanation on MDN: It creates a new object, using the existing object to provide the newly created object's __proto__
 function Parent(name) {
    this.name = name;
    this.colors = ['red', 'blue', 'yellow'];
}
Parent.prototype.getName = function() {
    console.log(this.name)
}

function Child(name, age) {
    // 核心代码①
    Parent.call(this, name);

    this.age = age;
}
// 核心代码②
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
Note the core code: Parent.call(this, name); and Child.prototype = Object.create(Parent.prototype);

Parasitic compositional inheritance, which combines the advantages of parasitic inheritance and compositional inheritance, is the most ideal inheritance paradigm for reference types.

7. Inheritance of classes in ES6

The class keyword was introduced in ES6, and inheritance can be achieved through the extends keyword.

 class Parent {}
class Child extends Parent {
    constructor(name, age, color) {
        // 调用父类的constructor(name, age)
        super(name, age);
        this.color = color;
    }
    toString() {
        return this.color + ' ' + super.toString(); // 调用父类的toString()
    }
}

The class keyword is just syntactic sugar for prototypes, JavaScript inheritance is still implemented based on prototypes.

refer to


IOneStar
3.1k 声望1.9k 粉丝