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
- Prototype chain inheritance
- Using Constructor Inheritance (Classic Inheritance)
- Combinatorial inheritance: prototype chain + borrowed constructor (most common)
- Prototype inheritance (Object.create)
- parasitic inheritance
Parasitic composition inheritance (ideal)
- 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:
- The method added to the constructor of the parent class can be accessed by the subclass
shortcoming:
- All properties from the prototype object are shared by all instances, child1 modifying the colors will affect the colors of child2
- 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);
andChild.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 executeParent.call(this, name);
, at this time it will call againParent
constructor
- Once is when setting the prototype of the subtype instance
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);
andChild.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.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。