In the previous summary, we analyzed the prototype “JS summary of the prototype” in detail, the prototype is very useful for simulating inheritance, this time, we talk about several ways of prototype inheritance.

function Person (age) {
  this.age = age || 18
}
Person.prototype.sleep = function () {
  console.log('sleeping')
}
Copy the code

🍖 Approach 1: Prototype chain inheritance (not recommended)

function Programmer() {} Programmer.prototype = new Person () Programmer.prototype.code = function () { console.log('coding') } let jon = new Programmer() jon.code() // coding jon.sleep() // sleeping jon instanceof Person //  true jon instanceof Programmer // true Object.getPrototypeOf(jon) // Person {age: 18, code: ƒ} Jon.__proto__ // Person {age: 18, code: ƒ}Copy the code

Disadvantages:

  1. Cannot pass a parameter to the parent constructor
  2. All attributes of the parent class are shared, and if one instance modifies an attribute, all other subclass instances are affected

🌭 Approach 2: Borrow constructors (classical inheritance) (not recommended)

Copy the attributes in the parent constructor

function Programmer(name) { Person.call(this) this.name = name } let jon = new Programmer('jon') jon.name // jon jon.age  // 18 jon.sleep() // Uncaught TypeError: jon.sleep is not a function jon instanceof Person // false jon instanceof Programmer // trueCopy the code

Advantages:

  1. You can pass arguments to a parent class
  2. Shared attributes are avoided

Disadvantages:

  1. It’s an instance of a subclass, not an instance of a superclass
  2. Methods are defined in the constructor and are created each time an instance is created

🍤 Approach 3: Combination Inheritance (recommended)

Composite stereotype chain inheritance and borrowed constructor inheritance.

function Programmer(age, name) { Person.call(this, Age). This name = name} Programmer. The prototype = new Person () Programmer. The prototype. The constructor = Programmer / / repair point constructor let jon = new Programmer(18, 'jon') jon.age // 18 jon.name // jon let flash = new Programmer(22, 'flash') flash.age // 22 flash.name // flash jon.age // 18 jon instanceof Person // true jon instanceof Programmer // true flash instanceof Person // true flash instanceof Programmer // trueCopy the code

Advantages: Combining the advantages of stereotype chain inheritance and constructors is the most common inheritance pattern in JavaScript

Disadvantages: The parent constructor is called twice

🌮 mode 4: Original type inheritance (not recommended)

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

let obj = {
  gift: ['a', 'b']
}

let jon = create(obj)
let xiaoming = create(obj)

jon.gift.push('c')
xiaoming.gift // ['a', 'b', 'c']
Copy the code

Disadvantages: Shared properties and methods

🍳 Approach 5: Parasitic inheritance (not recommended)

Create a function that encapsulates only the inheritance process, internally does the enhancement object in some form, and finally returns the object

function createObj (o) {
  var clone = Object.create(o)
  clone.sayName = function () {
    console.log('hi')
  }
  return clone
}
Copy the code

Disadvantages: As with the borrowed constructor pattern, methods are created every time an object is created

🍧 Approach 6: Parasitic combinatorial Inheritance (best)

The subclass constructor copies the parent class’s own attributes and methods, and the subclass stereotype accepts only the parent class’s stereotype attributes and methods:

function create(prototype) { function Super() {} Super.prototype = prototype return new Super() } function Programmer(age, name) { Person.call(this, age) this.name = name } Programmer.prototype = create(Person.prototype) Programmer.prototype.constructor = Programmer // The fix constructor points to let Jon = new Programmer(18, 'Jon ') jon.name // JonCopy the code

Advanced packaging:

function create(prototype) { function Super() {} Super.prototype = prototype return new Super() } function prototype(child, Parent) {let prototype = create(parent. Prototype) prototype.constructor = child  } function Person (age) { this.age = age || 18 } Person.prototype.sleep = function () { console.log('sleeping') } function Programmer(age, name) { Person.call(this, age) this.name = name } prototype(Programmer, Person) let jon = new Programmer(18, 'jon') jon.name // jonCopy the code

To quote the praise of parasitic combinatorial inheritance from JavaScript Advanced Programming:

The efficiency of this approach is that it calls the Parent constructor only once, and thus avoids creating unnecessary, redundant properties on Parent. Prototype. At the same time, the prototype chain stays the same; Therefore, instanceof and isPrototypeOf can also be used normally. Parasitic combinatorial inheritance is generally considered by developers to be the ideal inheritance paradigm for reference types.

🍜 Method 7: ES6 extends (Best)

Class Person {constructor(age) {this.age = age} sleep () {console.log('sleeping')} extends Person { constructor(age, name) { super(age) this.name = name } code () { console.log('coding') } } let jon = new Programmer(18, 'jon') jon.name // jon jon.age // 18 let flash = new Programmer(22, 'flash') flash.age // 22 flash.name // flash jon instanceof Person // true jon instanceof Programmer // true flash instanceof Person // true flash instanceof Programmer // trueCopy the code

Advantages: No manual prototyping.

Disadvantages: New syntax, as long as some browsers support it, needs to be converted to ES5 code.

🚀 reference

  • “JavaScript Advanced Programming (3rd edition)” 6.3 Inheritance
  • “Multiple ways of JavaScript Deep Inheritance and Pros and Cons” by Hu Yu
  • Introduction to ECMAScript 6 Class inheritance by Ruan Yifeng