前言
继承是面向对象技术中的一个概念,可以使得子类具有父类的属性和方法,同时可以重写父类的属性和方法。在JavaScript中,实现面向对象编程的方式与传统面向对象语言不同,主要体现在传统面向对象语言基于类,而JavaAcript基于原型和原型链机制来实现。因此,本篇主要讲解在JavaScript中,如何利用ES5的原型和原型链机制来实现继承这一特性。
正文
ES5中主要有四种实现方式,分别为原型链继承、借用构造函数继承、组合式继承、寄生式组合继承。这几种方式的发展顺序可以简单理解为循序渐进发展的,一般后一种都是补足了前一种的问题(可以对比下一种的优点和上一种的缺点来看);显而易见,寄生式组合继承是最这几种最优秀的一种继承方式。
第一种 原型链继承
1.1 是什么?
- 父类创建一个实例作为子类的原型对象。(如:Children.prototype = new Parent())
1.2 查找顺序
- 子类实例--->父类实例--->父类原型对象
1.3 优缺点
- 优点:可以访问父类原型对象上的属性。
- 缺点:
- 无法向父类传参(super()功能)
- 若new Parent()是引用类型,则修改时会影响所有的child实例。
第二种 借用构造函数继承
2.1 是什么?
- 在子类构造函数中执行父类函数,并为其绑定子类的this,理解代码如下:
function Children() {
Parent.apply(this, arguments)
}
2.2 查找顺序
- 子类实例--->子类原型对象--->父类构造函数
2.3 优缺点
- 优点:
- 避免实例之间共享一个原型实例
- 可以向父类构造方法传参
- 缺点:
- 获取不到父类原型对象上的属性和方法。(这个第一种方式(即原型链继承)可以获取)
过渡语:借用构造函数继承的缺点刚好原型链继承可以弥补,于是将他们两个组合使用就有了第三种方式,即组合式继承。
第三种 组合式继承(原型链+借用构造函数)
3.1 是什么?
- 将原型链式继承和借用构造函数继承组合使用。
3.2 优缺点
- 优点: 集合了上述两种方式的优点。
- 缺点:
- 每当创建子类时(const child1 = new Children()),原型链式继承(Children.prototype === new Parent())会执行一次Parent构造函数;Parent.apply(this, arguments)也会执行一次Parent构造函数。也就是说,会执行两次Parent构造函数。
- 原型链上会存在两份相同的属性和方法,不优雅。
第四种 寄生式组合继承(原型链+寄生式函数+构造函数继承)
3.1 是什么?
- 先来了解下Object.create(),以一个现有对象作为原型,创建一个新对象。
- 参数:它接收两个参数(proto, propertiesObject)其中第一个参数必选,为现有对象(并作为原型对象),第二个参数可选,是为新创建的对象添加具有对应属性名称的属性描述符。
- 返回值:返回根据指定的原型对象和属性创建的新对象。(这个对象可以通过.的方式来修改原型对象的属性和方法)
3.2 优缺点
- 优点: 将指向父类实例改为指向父类原型,进而减去一次构造函数执行。也就是:Children.prototype = Object.create(Parent.prototype),此处父类和子类并不共享同一个原型对象。
3.3 核心代码
Children.prototype = Object.create(Parent.prototype)
Children.prototype.construct = Children
综上,Es5中实现继承主要通过以上方式,其中寄生式组合继承最好,发展顺序如图。