Inheritance in ES5

Prototype chain inheritance

Use an instance of a parent class as a prototype for a subclass.

// SubType subclass SuperType
SubType.prototype = new SuperType();

SubType.prototype.constructor = SubType;
Copy the code

advantages

Superclass methods can be reused

disadvantages

  • All referenced attributes of the parent class are shared by subclasses. If you change the reference properties of one subclass, other subclasses will be affected.
  • When creating an instance of a subclass, you cannot pass arguments to the constructor of the parent class.

Constructor inheritance

The call() and apply() methods can be used in the subclass constructor to call the parent constructor.

SuperType.call(SubType);
Copy the code

advantages

Solve two disadvantages of prototype chain inheritance.

  • You can pass arguments to the parent class in the subclass constructor
  • Referenced properties in the parent constructor are not shared

disadvantages

  • Subclasses do not have access to methods defined on superclass prototypes because all method properties are written in constructors that are initialized each time an instance is created.

Combinatorial inheritance

function SuperType() {
	this.name = 'parent';
}

SuperType.prototype.say = function() {
	// ...
}

function SubType() {
	SuperType.call(this)	// Call SuperType a second time
}

SubType.prototype = new SuperType();	// Call SuperType the first time
Copy the code

Using stereotype chains to inherit properties and methods from stereotypes, and using constructors to inherit instance properties, allows methods to be defined on stereotypes for reuse and allows each instance to have its own properties.

It’s a combination of the two.

advantages

  • Methods of the parent class can be reused
  • You can pass arguments to the parent class in the subclass constructor
  • Referenced properties in the parent constructor are not shared

disadvantages

In any case, the superclass is called twice, once when the subclass prototype is created and once inside the subclass constructor.

Primary inheritance

Instead of strictly constructors, prototypes allow you to create new objects from existing objects. Essentially a shallow copy of the parameter object.

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

advantages

  • Superclass methods are reusable

disadvantages

  • Reference properties of the parent class are shared by all subclasses
  • Subclass instances cannot pass arguments to their parent classes

ECMAScript5 normalizes old-style inheritance by adding the object.create () method. This method takes two parameters: an object to be used as a prototype for the new object and, optionally, an object that defines additional properties for the new object. Object.create() behaves the same as Object () when passed a parameter.

Parasitic inheritance

Get a shallow copy of the target object using primitive inheritance, and then enhance the capability of the shallow copy.

function createAnother(original) {
	var clone = Object.create(original);
        // Enhance the function by adding new attributes and methods to the constructor
	clone.sayHi = function() {
    		cosole.log('HI');
     		returnclone; }}Copy the code

disadvantages

  • Can not solve the shortcomings of the original type inheritance
  • Similar to constructors, the inability to reuse methods reduces efficiency.

Parasitic combinatorial inheritance

It is essentially a copy of the parent’s prototype, so it does not include the parent’s constructor and therefore does not waste calling the parent’s constructor twice.

It’s a combination of combinatorial inheritance and parasitic inheritance.

function inheritPrototype(subType, superType) {
    // Create a shallow copy of the superclass prototype
    var prototype = Object.create(superType.prototype);
    // Fix the prototype constructor
    prototype.constructor = subType;
    // Replace the prototype of the subclass with this prototype
    subType.prototype = prototype;
}
Copy the code

advantages

  • The superclass constructor is called only once
  • Subclass instances can pass arguments to their parent classes
  • Superclass methods can be reused
  • References to the parent class are not shared by all subclasses

conclusion

In general, inheritance works in two ways:

1. Prototype sharing is achieved through prototype chain, that is, the prototype of a subclass points to an instance of its parent class.

2. Borrow constructors, that is, through JS apply, call implementation of the subclass to call the attributes of the parent class, method.

The prototype chain method can share all attribute methods, but can not achieve attribute and method exclusive.

Borrowing constructors can also pass arguments in subclass constructors in addition to attributes and methods, but the code cannot be reused.

Combined inheritance is to use the above two inheritance methods together. The shared attributes and methods are realized by prototype chain inheritance, and the exclusive attributes and methods are realized by borrowing constructors.

But! There is a minor bug with composite inheritance, which is implemented by calling the superclass twice.

We can see that the above three methods all use the parent class’s constructor directly, the stereotype chain method uses it as a prototype of the child class, and the constructor method copies its contents directly to the child class constructor.

So a new idea has emerged — prototypal inheritance. Instead of using strict constructors, prototypes allow you to create new objects from existing objects, that is, subclass them by making a shallow copy of the prototype object.

Parasitic inheritance, on the other hand, enhances the function by adding new attributes and methods to the constructor inside the function. You want to customize attributes and functions for subclasses, similar to constructor inheritance.

Combining all of the above, we get parasitic combinatorial inheritance. This approach addresses all of the above shortcomings by using parasitic inheritance to inherit from the supertype stereotype and then assign the result to the subtype stereotype.

Inheritance in ES6

ES6 inheritance is similar to parasitic combinatorial inheritance in that it is essentially a syntactic sugar implemented through the extends keyword.

class A(a){}

class B extends A {
    constructor() {
    
        super();
        
    }
}
Copy the code

Subclasses must call the super method from constructor or they will get an error when creating a new instance. This is because the subclass doesn’t have its own This object. Instead, it inherits the parent’s this object and processes it. If you don’t call super, the subclass doesn’t get this.

Realize the principle of

class B extends A {}

B.__proto__ === A;    // Inherit attributes
B.prototype.__proto__ === A.prototype;	// Inheritance method
Copy the code

An inheritance statement has two inheritance chains, one for property inheritance and one for method inheritance.

What are the differences between ES5 function inheritance /ES6 classes other than how they are written?

  • Class does not have variable promotion
new Foo();	//ReferenceError
class Foo {}
Copy the code

The reason ES6 does not promote variable declarations to the code header is to ensure that subclasses are defined after their parent class.

  • Classes and modules use strict mode internally by default

  • All methods defined internally by a class are not enumerable. (Both static and instance methods)

class Point {
    constructor(x, y) {
        // ...
    }
    
    toString() {
		// ...}}Object.keys(Point.prototype)	/ / []

Object.getOwnPropertyNames(Point.prototype)	// ["constructor", "toString"]
Copy the code
  • Class must be called using new, otherwise an error is reported
class Foo {
  constructor() {
    this.foo = 40; }}const foo = Foo(); // TypeError: Class constructor Foo cannot be invoked without 'new'
Copy the code
  • Class all methods (including static and instance methods) have no prototype object and cannot be called with new.
class Foo {
  constructor() {
    this.foo = 40;
  }
  print() {
    console.log(this.foo); }}const foo = new Foo();
const fooPrint = new foo.print(); // TypeError: foo.print is not a constructor
Copy the code
  • Different inheritance methods
function Super() {}
function Sub() {}

Sub.prototype = new Super();
Sub.prototype.constructor = Sub;

var sub = new Sub();

// The prototype chain of the subclass constructor points not to the parent class constructor, but to the function.prototype one layer above
Sub.__proto__ === Function.prototype;
Copy the code

class Super {}
class Sub extends Super {}

const sub = new Sub();

// The prototype chain of the subclass constructor points directly to the parent class constructor
Sub.__proto__ === Super;
Copy the code