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.
🌶 premise
With a parent class as the premise, enumerate the js inheritance method:
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:
- Cannot pass a parameter to the parent constructor
- 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 // true
Copy the code
Advantages:
- You can pass arguments to a parent class
- Shared attributes are avoided
Disadvantages:
- It’s an instance of a subclass, not an instance of a superclass
- 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.prototype = new Person()
Programmer.prototype.constructor = Programmer // Fix constructor pointing
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 // true
Copy 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 // Fix constructor pointing
let jon = new Programmer(18.'jon')
jon.name // jon
Copy 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 // Fix constructor pointing
child.prototype = prototype
}
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 // jon
Copy 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)
/ / parent class
class Person {
constructor(age) {
this.age = age
}
sleep () {
console.log('sleeping')}}/ / subclass
class Programmer 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 // true
Copy 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