继承的目的是什么?结果是什么?
子类继承父类,子类实例拥有和父类实例相同的属性
父类实例的属性来自于哪里?
- 构造函数
- 原型对象
因此继承的实现应该做到以下两件事情
- 继承父类构造函数设置的属性: 借用父类构造函数使用apply/call绑定this为子类实例,使得子类实例具有父类实例相同属性
- 继承父类的原型属性: 将父类的原型属性设置到子类原型上去,通过new关键字调用继承父类原型属性
原型继承
思路是继承将父类实例放在构造函数原型上,父类实例作为子类原型
缺陷是所有子类共用一个原型对象,引用类型属性的地址相同。
只继承了原型属性,但缺失了调用父类构造函数为子类实例设置属性的这一过程,导致继承的属性全部来自于原型
function object(o){
function F(){}
F.prototype = o
return new F()
}
const o = {a:[1,2,3]}
const newO = object(o)
newO.a.push(4)
console.log(newO,o); // [1,2,3,4]
组合继承
原型继承+借用父类构造函数设置实例属性
用的最多,缺陷是调用了两次父类构造函数
function SuperClass() { }
function SubClass() {
SuperClass.apply(this, Array.from(arguments)) // 借用构造函数
}
SubClass.prototype = new SuperClass() // 继承原型属性
寄生式继承
寄生式继承基于原型继承, 主要体现在给实例做拓展与与增强
缺陷在于增强实例的过程中不能做到函数复用,使得效率降低,这一点和构造函数设置实例属性是一致的
function object(o){
function F(){}
F.prototype = o
const obj = new F()
// 拓展、增强
obj.fn = ()=>console.log('fn');
return obj
}
寄生组合式继承
寄生组合式继承解决了组合继承调用两次父类构造函数使得开销过大的问题
function extend(SubClass, SuperClass) {
function F() { }
F.prototype = SuperClass.prototype
SubClass.prototype = new F()
SubClass.prototype.constructor = SubClass
SubClass.SuperClass = SuperClass
}
// 使用
function Child() {
Child.SuperClass.apply(this, arguments)
}
function Parent() { this.a = 1 }
extend(Child, Parent)
console.log(new Child());
总结
原型继承,所有实例共用一个原型对象,继承了原型属性,但缺失了调用父类构造函数为子类实例设置属性的这一过程,导致继承的属性全部来自于原型,缺少了文章开头提到的第1点
组合继承,用的最多的方案,虽然1、2都满足,但是存在调用了两次父类构造函数的问题
寄生式继承,在原型继承的基础上,在实例创建的过程中给实例做拓展和增强,缺陷为并没有调用父类构造函数设置实例属性,复用性差,不满足1
寄生组合式继承,通过一个中间函数F,解决了组合继承中父类构造函数调用两次的问题;在子类实例创建过程中调用父类构造函数,解决了原型继承和寄生式继承中没有用父类构造函数为子类实例设置属性的问题。 1、2都满足。
原创文章,作者:dweifng,如若转载,请注明出处:https://blog.ytso.com/tech/webdev/272725.html