This article lists several common inheritance methods in JS, including the EXTEND inheritance in ES6.

1. Prototype chain inheritance

// create a constructor function Father() {this.name = "SMD "; this.arr = [1, 2, 3]; } Father.prototype.fnA = function() { console.log('a'); Function Child() {this.selfarr = ['a', 'b', 'c']; Prototype Child. Prototype = new Father();} // create an instance of the Father function and assign the address to the inheritor. var f1 = new Father(); var f2 = new Father(); var c1 = new Child(); var c2 = new Child(); f1.arr.push(4); f2.arr.push(5); c1.arr.push(4); c2.arr.push(5); c1.selfArr.push('d'); console.log(f1.arr); // [1, 2, 3, 4] console.log(f2.arr); // [1, 2, 3, 5] console.log(c1.arr); // [1, 2, 3, 4, 5] console.log(c2.arr); // [1, 2, 3, 4, 5] console.log(c1.selfArr); // ['a', 'b', 'c', 'd'] console.log(c2.selfArr); // ['a', 'b', 'c']Copy the code

This figure refers to eight inheritance schemes commonly used in JavaScript. SuperType corresponds to Father and SubType corresponds to Child.

Disadvantages:

  • The constructor of the subclass points to the parent class, which requires you to rewrite construtor manually.

  • The address of the reference type attribute is different between instances of new, but stereotype chain inheritance will cause the reference type of the subclass instance inherited from the parent class to tamper with each other.

2. Inheritance by constructor

function Father(name, age) { this.name = name; this.age = age; this.arr = [1, 2, 3]; } function Child() {Father Father. Call (this); } var f1 = new Father(); var c1 = new Child(); var c2 = new Child(); c1.arr.push(4); c2.arr.push(5); console.log(f1.arr); // [1, 2, 3] console.log(c1.arr); // [1, 2, 3, 4] console.log(c2.arr); // [1, 2, 3, 5]Copy the code

Disadvantages:

  • Subclasses can only inherit instance properties and methods of their parent class, not stereotype properties/methods

  • Unable to reuse, each subclass has a copy of the parent class instance function, affecting performance

3. Combinatorial inheritance

Composite inheritance is a combination of prototype chain inheritance and borrowed constructor inheritance. The inheritance of prototype attribute and method is realized by prototype chain, and the inheritance of instance attribute is realized by borrowing constructor technique.

function Father(name, age) { this.name = name; this.age = age; this.arr = [1, 2, 3]; } Father.prototype.sayName = function () { console.log(this.name); } function Child(name, age, job) {// Child(name, age, job); this.job = job; Parent.prototype = new Father(); parent.prototype = new Father(); / / rewrite the Child constructor Child. The prototype. The constructor = Child; Child.prototype.sayAge = function () { console.log(this.age); } var c1 = new Child('wwx', 30, 'FE'); c1.arr.push(4); console.log(c1.arr); // [1, 2, 3, 4] c1.sayName(); // wwx c1.sayAge(); // 30 var c2 = new Child('www', 20, 'FM'); c2.arr.push(5); console.log(c2.arr); // [1, 2, 3, 5] c2.sayName(); // www c2.sayAge(); / / 20Copy the code

Disadvantages:

  • The first call to Father gives child. prototype two attributes name and age.

  • The second call to Father writes the name and age attributes to instance C1.

Two attributes on c1 mask two attributes of the Father. Prototype. Therefore, the disadvantage of the composite pattern is that when subclasses are used to create instance objects, there are two copies of the same property/method in the prototype (name and age in the figure above).

4. Original type inheritance

Assign an object directly to the prototype of the empty object constructor using an empty object as a mediator.

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

CreateObj () makes a shallow copy of the object passed in, pointing the prototype of the constructor F directly to the object passed in.

var person = {
  name: 'wwx',
  age: 30,
  arr: [1, 2, 3]
}

var onePerson = createObj(person);
onePerson.name = 'Jack';
onePerson.arr.push(4);

var anotherPerson = createObj(person);
anotherPerson.name = 'Rose';
anotherPerson.arr.push(5);
console.log(onePerson.name);  // Jack
console.log(onePerson.arr);  // [1, 2, 3, 4, 5]
console.log(anotherPerson.name);  // Rose
console.log(anotherPerson.arr); // [1, 2, 3, 4, 5]
Copy the code

Disadvantages:

  • Reference type attributes inherited from multiple instances of Person point to the same and are subject to tampering.

  • Unable to pass parameters

In addition, there is an object.create () method in ES5 that can replace the createObj method above.

Parasitic inheritance

Core: On the basis of the original type inheritance, enhance the object, return the constructor.

function createObj(o) { function F() {} F.prototype = o; return new F(); } function createAnother(original){var clone = createObj(original); SayHi = function(){alert("hi"); }; return clone; // Return this object}Copy the code

The function strengthens the constructor by adding new attributes and methods.

function createObj2(o) {
  var obj = createObj(o);
  obj.sayHi = function() {
    alert('hi');
  }
  return obj;
}


var person = {
  name: "wwx",
  arr: [1, 2, 3]
};
var anotherPerson = createAnother(person);
anotherPerson.sayHi(); // "hi"
Copy the code

Disadvantages (same as the original type inheritance) :

  • Reference type attributes inherited from multiple instances of Person point to the same and are subject to tampering.

  • Unable to pass parameters

Parasitic combinatorial inheritance

Inheritance with a combination of borrowed constructors passing parameters and parasitic patterns:

function createObj (o) { function F () {}; F.prototype = o; return new F(); } function inheritProtorype(Child, Father) {var prototype = createObj(Father. Prototype); Constructor = prototype.constructor = Child; // Assign the newly created object to the subclass's prototype child. prototype = prototype; } function Father(name) { this.name = name; this.arr = [1, 2, 3, 4]; } Father. Prototype. SayName = function() {console.log(' parent '+ this.name); } function Child(name, age) {Father. Call (this, name); this.age = age; } inheritProtorype(Child, Father); Child.prototype.sayAge = function() { console.log(this.age); } var child1 = new Child('ww', 10); var child2 = new Child('xx', 20); child1.arr.push(5); console.log(child1.name); // 'ww' console.log(child2.name); // 'xx' console.log(child1.arr); // [1, 2, 3, 4, 5] console.log(child2.arr); // [1, 2, 3, 4]Copy the code

The efficiency of this example is that it calls the Father constructor only once and thus avoids creating unnecessary, redundant attributes on Child.prototype. At the same time, the prototype chain stays the same; Therefore, instanceof and isPrototypeOf() can also be used normally.

Advantages:

  • Multiple inheritance can solve the problem of two invocation solution instances sharing reference types the prototype chain remains the same

7. Blending inherits multiple objects

function MyClass() { SuperClass.call(this); OtherSuperClass.call(this); Prototype = object.create (superclass.prototype); / / mixed other Object. The assign (MyClass. Prototype, OtherSuperClass. Prototype); / / to specify the constructor MyClass. Prototype. Constructor = MyClass; MyClass.prototype.myMethod = function() { // do something };Copy the code

Object.assign copies functions from the OtherSuperClass prototype to the MyClass prototype, making all instances of MyClass available to OtherSuperClass methods.

8. ES6 class extends extends

The extends keyword is used primarily in class declarations or class expressions to create a class that is a subclass of another class. A class can have only one constructor, and SyntaxError is raised if more than one constructor is specified. If no constructor is explicitly specified, the default constructor method is added, as shown in the following example.

class Rectangle { // constructor constructor(width, height) { this.width = width; this.height = height; } // Getter get area() { return this.calcArea() } // Method calcArea() { return this.height * this.width; } } const rectangle = new Rectangle(10, 20); console.log(rectangle.area); Rectangle extends Rectangle {constructor(width, height) {super(width, height); // Call the super method this.name = 'Square' before using the constructor 'this '; } get area() { return this.height * this.width; } } const square = new Square(10, 10); console.log(square.area); / / 100Copy the code

The core code of extends inheritance is as follows and is implemented in the same way as the parasitic combinational inheritance described above.

Function _inherits(subType, superType) {function _inherits(subType, superType) {function _inherits(subType, superType) { Prototype = object. create(superType && supertype. prototype, {constructor: {value: constructor:) subType, enumerable: false, writable: true, configurable: true } }); if (superType) { Object.setPrototypeOf ? Object.setPrototypeOf(subType, superType) : subType.__proto__ = superType; }}Copy the code

The difference between function declarations and class declarations

  • Class can’t be promoted, function can be promoted

  • Class must be called new and cannot be executed directly

  • Stereotype methods in class cannot be enumerated

ES5 inheritance and ES6 inheritance

  • ES5 inheritance essentially creates an instance object of the subclass and then adds the Parent class’s methods to this (parent.call (this)).

  • ES6 inheritance is different, essentially creating an instance object of the parent class, this, and then modifying this with the constructor of the subclass. Because subclasses do not have their own this object, they must first call the super() method of the superclass, or new instances will fail.

Refer to the article

Some of the content and images of this article are reprinted from the following articles:

There are eight inheritance schemes commonly used in JavaScript