如何实现继承

继承是指子类拥有父类的所有属性与方法,而无需重新定义。

class实现继承

es6新引入的class关键字具有正式定义类的能力,class之间通过使用extends关键字实现继承,这比通过修改原型链实现继承,要方便清晰很多

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Parent {
// constructor关键字用于在类定义块内部创建类的构造函数,使用new操作符创建类的新实例时,会调用这个函数
constructor(name) {
this.name = name;
}
sayHello() {
console.log('hello');
}
sayName() {
console.log('my name is ' + this.name);
}
}
class Child extends Parent {
constructor(name, age) {
// super()的行为如同调用构造函数,如果需要给父类构造函数传参,则需要手动传入,例如此处的name参数
super(name);
this.age = age;
}
sayAge() {
console.log('my age is ' + this.age);
return this.age;
}
}
let child = new Child('小明', 23)
child.sayHello() // hello
child.sayName() // my name is 小明
child.sayAge() // my age is 23

原型链继承

思路:将父类的实例作为子类的原型
缺点:来自原型对象的所有属性被所有实例共享

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function Parent(name) {
this.name = name;
this.colors = ["red", "blue", "green"];
this.sayHello = function () {
console.log('hello');
}
}
Parent.prototype.sayHi = function () {
console.log('hi');
};
function Child(name, age) {
this.age = age;
}
Child.prototype = new Parent();
let child = new Child('小明', 23);
child.colors.push('black')
console.log(child.name); // 小明
console.log(child.age); // 23
console.log(child.colors); // ['red', 'blue', 'green', 'black']
child.sayHello() // hello
child.sayHi() // hi

let child1 = new Child('小明1', 23);
console.log(child1.colors); // ['red', 'blue', 'green', 'black']

构造继承(经典继承)

思路:在子类构造函数中调用父类构造函数
缺点:只能继承父类的实例属性和方法,不能继承原型属性/方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function Parent(name) {
this.name = name;
this.sayHello = function () {
console.log('hello');
}
}
// 此处可以引申,方法挂载到函数内部和定义到原型上有什么区别
// 挂载在函数内部的方法,实例内部会复制构造函数的方法,存在浪费内存;而通过原型法添加方法的好处是:所有对象都共享,节约内存
Parent.prototype.sayHi = function () {
console.log('hi');
};
function Child(name, age) {
Parent.call(this, name);
this.age = age;
}
let child = new Child('小明', 23);
console.log(child.name); // 小明
console.log(child.age); // 23
child.sayHello() // hello
child.sayHi() // Uncaught TypeError: child.sayHi is not a function

组合继承

组合继承弥补了原型链和盗用构造函数的不足,是JavaScript中使用最多的继承模式。而且组合继承也保留了instanceof操作符和isPrototypeOf()方法识别合成对象的能力。

思路:使用原型链继承原型上的属性和方法,而通过盗用构造函数继承实例属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function Parent(name) {
this.name = name;
this.sayHello = function () {
console.log('hello');
}
}
Parent.prototype.sayHi = function () {
console.log('hi');
};
function Child(name, age) {
Parent.call(this, name);
this.age = age;
}
Child.prototype = new Parent(); // 重写原型对象,Child.prototype赋值为Parent的实例
Child.prototype.constructor = Child; // 组合继承需要修复构造函数指向,不然会导致 child.constructor === Parent
let child = new Child('小明', 23);
console.log(child.name); // 小明
console.log(child.age); // 23
child.sayHello() // hello
child.sayHi() // hi

原型式继承

思路:实现思路就是将子类的原型设置为父类的原型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function Parent(name) {
this.name = name;
this.sayHello = function () {
console.log('hello');
}
}
Parent.prototype.sayHi = function () {
console.log('hi');
};
function Child(name, age) {
// 通过 call 继承父类的实例属性和方法,不能继承原型属性/方法
Parent.call(this, name)
this.age = age;
}
// Object.create 方法接受传入一个作为新创建对象的原型的对象,创建一个拥有指定原型和若干个指定属性的对象
// 通过这种方法指定的任何属性都会覆盖原型对象上的同名属性
Child.prototype = Object.create(Parent.prototype, {
constructor: {
value: Child,
enumerable: false,
writable: true,
configurable: true
}
})
let child = new Child('小明', 23);
console.log(child.name); // 小明
console.log(child.age); // 23
child.sayHello() // hello
child.sayHi() // hi

寄生式组合继承

思路:借用构造函数继承属性,通过原型链的混成形式来继承方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function Parent(name) {
this.name = name;
this.sayHello = function () {
console.log('hello');
}
}
Parent.prototype.sayHi = function () {
console.log('hi');
};
function Child(name, age) {
Parent.call(this, name)
this.age = age;
}
Child.prototype = Object.create(Parent.prototype)
Child.prototype.constructor = Child;
let child = new Child('小明', 23);
console.log(child.name); // 小明
console.log(child.age); // 23
child.sayHello() // hello
child.sayHi() // hi

参考资料:
JS 继承的 六 种实现方式


如何实现继承
https://xypecho.github.io/2021/10/26/如何实现继承/
作者
很青的青蛙
发布于
2021年10月26日
许可协议