002 图解继承
CENSWIN 8/13/2021 Javascript
本文讲解JavaScript各种继承方式和优缺点
# 原型继承
原型链继承
function Parent() { this.name = ["Allen", "Diaz"]; } Parent.prototype.getNmae = function () { console.log(this.name); }; function Child() {} Child.prototype = new Parent(); const instanceA = new Child(); instanceA.name.push('Bob'); const instanceB = new Child(); console.log(instanceA.name); // ["Allen", "Diaz", "Bob"] console.log(instanceB.name); // ["Allen", "Diaz", "Bob"]1
2
3
4
5
6
7
8
9
10
11
12
13
14缺点:
- 引用类型的
name属性被实例AinstanceA与 BinstanceB共享导致instanceB。name也被修改 - 在实例化时,无法向
Parent传值

- 引用类型的
经典继承(利用构造函数)
function Parent() { this.name = ["Allen", "Diaz"]; } function Child() { Parent.call(this) } const instanceA = new Child(); instanceA.name.push("Bob"); const instanceB = new Child(); console.log(instanceA.name); // ["Allen", "Diaz", "Bob"] console.log(instanceB.name); // ["Allen", "Diaz"]1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
优点
- .避免了引用类型的属性被所有实例共享
- 可以在 Child 中向 Parent 传参
缺点
- 方法都在构造函数中定义,每次创建实例都会创建一遍方法
组合继承
将上述两者进行结合
function Parent(val) { this.value = val this.name = ["Allen", "Diaz"]; } Parent.prototype.getName = function () { console.log(this.name) } function Child(age, value) { Parent.call(this, value) this.age = age } Child.prototype = new Parent() const instanceA = new Child(18, 'testAA') instanceA.name.push('Bob') const instanceB = new Child(19, 'testBB') console.log(instanceA) console.log(instanceB) instanceA.getName(); // ["Allen", "Diaz", "Bob"]1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
优点:融合原型链继承和构造函数的优点,是 JavaScript 中最常用的继承模式。
寄生组合式继承
组合继承最大的缺点是会调用两次父构造函数
一次是设置子类型实例的原型的时候:
Child.prototype = new Parent();一次在创建子类型实例的时候:
var child1 = new Child('kevin', '18');那么我们该如何精益求精,避免这一次重复调用呢?
如果我们不使用 Child.prototype = new Parent() ,而是间接的让 Child.prototype 访问到 Parent.prototype 呢?
function Parent(name) { this.name = name; this.colors = ['red', 'blue', 'green']; } Parent.prototype.getName = function () { console.log(this.name) } function Child(name, age) { Parent.call(this, name); this.age = age; } // Object.create 的模拟实现 var F = function () {}; F.prototype = Parent.prototype; Child.prototype = new F(); var instanceA = new Child('kevin', '18'); console.log(instanceA);1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
封装继承方法
function prototype(child, parent) { var prototype = Object.create(parent.prototype); prototype.constructor = child; child.prototype = prototype; } // 当我们使用的时候: prototype(Child, Parent);1
2
3
4
5
6
7
8以上继承实现的核心就是将父类的原型赋值给了子类,并且将构造函数设置为子类,这样既解决了无用的父类属性问题,还能正确的找到子类的构造函数。
# Class 继承
以上两种继承方式都是通过原型去解决的,在 ES6 中,我们可以使用 class 去实现继承,并且实现起来很简单
class Parent {
constructor(value) {
this.val = value
}
getValue() {
console.log(this.val)
}
}
class Child extends Parent {
constructor(value) {
super(value)
}
}
const a = new Parent()
console.log(a)
const b = new Child('1')
console.log(b)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class 实现继承的核心在于使用 extends 表明继承自哪个父类,并且在子类构造函数中必须调用 super,因为这段代码可以看成 Parent.call(this, value)。