前几天去面试,被问到了js继承方面的问题,由于自己很久没去关注过这块了,导致回答得不尽人意!= =,因此本篇文章就准备重新梳理下这块的知识,废话不多说,开搞!

ppx.jpg

es5里的继承

在es6的class出现之前,都是通过一些hack的方式实现继承的,具体来说,主要有如下三种

接下来就分别聊聊它们各自的实现

qidai.jpeg

原型链继承

顾名思义,主要是通过 原型链 的方式来实现继承,核心关键点就是 将父类的实例赋值给子类的原型对象,下面给出代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function Parent(name) {
this.name = name
this.list = [1,2,3]
}
Parent.prototype.getName = function(){
return this.name
}

function Child(age) {
this.age = age
}
//关键步骤
Child.prototype = new Parent('name')
Child.prototype.constructor = Child

var instance = new Child(12)
instance.getName() //输出 'name'

这种方式的优点如下:

  1. 子类可以共享父类原型对象上的方法,节省内存
  2. 实现方式很简单

缺点如下:

  1. 由于父类的实例属性被共享,会存在子类实例同时修改父类实例属性的情况,从而导致冲突

构造函数继承

顾名思义,这种方式是借用 构造函数 来继承的,而不是依靠原型对象,下面给出代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 function Parent(name) {
this.name = name
this.list = [1,2,3]
this.getName = function(){
return this.name
}
}

function Child(age) {
//关键步骤
Parent.call(this,'name')
this.age = age
}

var instance = new Child(12)
instance.getName() //输出 'name'

这种方式优点如下:

  1. 由于子类实例 各自 拥有父类实例属性的一个副本,因此可以避免原型链继承中的冲突问题
  2. 实现方式简单

缺点如下:

  1. 由于父类实例属性和方法都各自生成一个副本存在于子类实例中,因此无法高效地进行 方法 复用,导致浪费内存空间

组合式继承

这种方式结合了上述两种方式的优点

  1. 利用原型链继承实现 方法的复用
  2. 利用构造函数继承实现 属性的独立副本

代码示例如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  function Parent(name) {
this.name = name
this.list = [1,2,3]
}
Parent.prototype.getName = function(){
return this.name
}

function Child(age) {
Parent.call(this,'name')
this.age = age
}
Child.prototype = new Parent('name')
Child.prototype.constructor = Child

var instance = new Child(12)
instance.getName() //输出 'name'

这种方式的唯一缺点就是 子类的原型对象上始终存在与其实例上一样的属性

es6里的继承

es6里的继承是官方推出的标准方式,主要利用 classextends 关键字来实现,代码示例如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Parent {
constructor(name) {
this.name = name
}
}

class Child extends Parent{
constructor(name,age) {
super(name)
this.age = age
}
}

const instance = new Child('name',12)
instance.name // 输出 'name'

在es6里的继承,需要注意的一点是 子类是没有自己的this,都是通过super来继承父类的this,然后在自己的constructor里修改继承过来的this

因此在子类的constructor里,super必须在使用到this的地方 之前 去调用,否则就会报错,super的本质就是 父类的constructor函数

结语

前端领域的知识浩如烟海,只有不断的学习与巩固才能真正领悟,学习力很重要,希望我和正在阅读本文的你都会拥有,好啦,over!