# 了解一下继承的方式?
- 多继承
- 传参
- 继承父类构造函数属性(实际上能传参就能继承)
- 继承父类原型属性(实例共享父类的原型,但可以实现多态),
- 复用父类构造函数(每次创建子类实例的时候不要重复调用父类构造函数)
个人感觉第 5 点非必要,因为复用则必然不可以多次传参,既然复用了就只能调用一次父类构造函数,所以传参也无意义了
因此我这里主要是讨论多继承,传参,原型属性这三个
# 原型链继承
核心:将父类的实例作为子类的原型
特点:子类实例继承了 父类构造函数属性、父类原型属性
缺点:
继承单一,无法实现多继承
无法传参
丢失子类实例 constructor 属性
function Father() {}
function Child() {}
Child.prototype = new Father()
2
3
# 借用构造函数继承
核心:在子类构造函数中调用父类构造函数,改变父类构造函数的 this 指向
特点:
- 只继承了父类构造函数属性(结合 new 原理理解:改变 prototype,调用构造函数)
- 可以传参,多继承
缺点:没有继承父类原型属性
function Father() {}
function Child() {
Father.call(this)
}
2
3
4
# 组合继承(前两种结合)
核心: 结合原型链继承和借用构造函数继承
特点: 多继承,传参,继承父类原型属性,父类构造属性
缺点:
- 丢失子类实例 constructor 属性
- 调用了两次构造函数
function Father() {}
function Child() {
Father.call(this)
}
Child.prototype = new Father()
2
3
4
5
# 原型式继承(object.create 原理)
核心:用一个函数包装一个对象,然后返回这个函数的调用,这个函数就变成了个可以随意增添属性的实例或对象。object.create()就是这个原理。
特点:
- 只继承了父类原型属性
- 没有 new 调用,直接继承父类产生实例
- 没有子类构造函数,也就没有 constructor 丢失的问题
缺点:不能传参,不能多继承
function extendPrototype(obj) {
// 类似原型链继承,但这里只是继承了父类原型属性
// 其实就是利用一个空属性的构造函数来 除去 构造函数属性,只留下原型属性
function F() {}
F.prototype = obj
return new F()
}
function Father() {}
var parent = new Father() //父类实例
var child = extendPrototype(parent)
2
3
4
5
6
7
8
9
10
# 寄生式继承(其实就是将一个新对象混入到父类中并返回)
核心:给原型式继承套个壳子而已
特点:
- 只继承了父类原型属性
- 不用 new 调用也可以达到相同的效果
缺点:不能传参,不能多继承
function extendPrototype(obj) {
function F() {}
F.prototype = obj
return new F()
}
function Father() {}
function Child() {
//直接返回子类实例
var parent = new Father()
var child = extendPrototype(parent)
//在这里给新对象增加属性和方法
child.a = 'a'
child.b = 'b'
return child
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 寄生组合式继承(终极方案)
核心:子类原型是另一个只继承父类原型属性的实例,修复 constructor
寄生组合式继承的高效率体现在它只调用了一次superClass
构造函数,并且因此避免了在subClass.prototype
上创建不必要的、多余的属性。于此同时,原型链还能保持不变;因此,还能够正常使用instanceof
和isPrototypeOf()
这是最成熟的方法,也是现在库实现的方法
特点:
- 继承了父类原型属性
- 可传参
- 多继承
function extend(subClass, superClass) {
function extendPrototype(obj) {
function F() {}
F.prototype = obj
return new F()
}
subClass.prototype = extendPrototype(superClass.prototype) //继承父类原型 constructor被覆盖
subClass.prototype.construtor = subClass //修复constructor
}
function Father() {}
function Child() {
Father.call(this)
}
extend(Child, Father)
2
3
4
5
6
7
8
9
10
11
12
13
14
# ES6 类继承 extends
extends
关键字主要用于类声明或者类表达式中,以创建一个类,该类是另一个类的子类。其中constructor
表示构造函数,一个类中只能有一个构造函数,有多个会报出SyntaxError
错误,如果没有显式指定构造方法,则会添加默认的 constructor
方法
extends
继承的核心代码如下,其实现和上述的寄生组合式继承方式一样
function extend(subClass, superClass) {
// 创建对象,创建父类原型的一个副本
// 指定对象,将新创建的对象赋值给子类的原型
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
enumerable: false,
writable: true,
configurable: true
}
})
// 增强对象,弥补因重写原型而失去的默认的constructor 属性
if (superClass) {
Object.setPrototypeOf
? Object.setPrototypeOf(subClass, superClass)
: (subClass.__proto__ = superClass)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 面试点
1、函数声明和类声明的区别
函数声明会提升,类声明不会。首先需要声明你的类,然后访问它,否则像下面的代码会抛出一个 ReferenceError。
let p = new Rectangle()
// ReferenceError
class Rectangle {}
2
3
4
2、ES5 继承和 ES6 继承的区别
- ES5 的继承实质上是先创建子类的实例对象,然后再将父类的方法添加到 this 上(Parent.call(this)).
- ES6 的继承有所不同,实质上是先创建父类的实例对象 this,然后再用子类的构造函数修改 this。因为子类没有自己的 this 对象,所以必须先调用父类的 super()方法,否则新建实例报错。
# 参考
← reduce setTimeout →