es5中类的继承
2019-03-15 本文已影响0人
啧啧扬
本篇在实现方面主要有以下五种方法
- 借助构造函数实现继承
- 借助原型链构造继承
- 方法1和方法2的组合方式
- 组合继承优化方式1
- 组合继承优化方式2(寄生组合式继承)
1. 借助构造函数实现继承
原理:在子类中通过call()调用父级构造函数,将父级构造函数的this指向子类的实例中
function Parent1(){
this.name = 'parent1'
}
function Child1(){
Parent1.call(this)//将父级构造函数的this指向子构造函数的实例上去
this.type = 'child1'
}
var child1 = new Child1()
console.log(child1)
//Child1 {name: "parent1", type: "child1"}
缺点:在Parent1原型链中的方法,子类拿不到,即没有继承父类的原型链。比如
Parent1.prototype.say=function(){}
console.log(child1.say)
//undefined
2. 借助原型链构造继承
原理:让子类实例的原型对象指向父类的实例,解决方法1中不会继承原型链的问题
function Parent2(){
this.name = 'parent2'
this.play = [1,2,3]
}
function Child2(){
this.type = 'child2'
}
Child2.prototype = new Parent2()
缺点:每个Child2实例的原型链都是同一个Parent2的实例。比如:
var s1 = new Child2()
var s2 = new Child2()
console.log(s1.play,s2.play)
s1.play.push(4)
console.log(s1.play,s2.play)
//[1,2,3] [1,2,3]
//[1,2,3,4] [1,2,3,4]
3. 方法1和方法2的组合方式
原理:结合方法1和方法2,让各个子类能分别获得父类的属性,同时继承父类的原型链
function Parent3(){
this.name = 'parent3'
this.play = [1,2,3]
}
function Child3(){
Parent3.call(this);
this.type = 'child3'
}
Child3.prototype = new Parent3()
var s3 = new Child3()
var s4 = new Child3()
s3.play.push(4)
console.log(s3.play,s4.play)
//[1, 2, 3, 4] [1, 2, 3]
console.log(s3.__proto__)
//Parent3 {name: "parent3", play: Array(3)}
缺点:父类的构造函数在call(this)和new Parent3()都执行了赋值,有重复赋值的问题
4. 组合继承优化方式1
原理:在组合方式下,将Child的原型链改为引用Parent4的原型链,避免构造函数执行两次
function Parent4(){
this.name = 'parent4'
this.play = [1,2,3]
}
function Child4(){
Parent4.call(this);
this.type = 'child4'
}
Child4.prototype = Parent4.prototype
var s5 = new Child4()
var s6 = new Child4()
console.log(s5.__proto__)
//{constructor: ƒ}
//constructor: ƒ Parent4()
//__proto__: Object
缺点:如上面的s5.__proto__.constructor,不能判断生成的对象是由子类实例化的还是父类实例化的;因为其constructor是指向父类的
console.log(s5 instanceof Child4, s5 instanceof Parent4)
console.log(s5.constructor)
//true true
// ƒ Parent4(){
// this.name = 'parent4'
// this.play = [1,2,3]
// }
5. 组合继承优化方式2(寄生组合式继承)
原理: 通过Object.create
让子类的原型调用的不是父类的原型,从而实现父类和子类原型对象的隔离。并修改子类原型constructor指向,使其指向子类构造函数
function Parent5(){
this.name = 'parent5'
this.play = [1,2,3]
}
function Child5(){
Parent5.call(this);
this.type = 'child5'
}
Child5.prototype = Object.create(
Parent5.prototype,
constructor: {
value: Child,
enumerable: false,
writable: true,
configurable: true
})
Child5.prototype.constructor = Child5;
var s7 = new Child5()
console.log(s7 instanceof Child5,s7 instanceof Parent5)
console.log(s7.constructor)
// true true
// ƒ Child5(){
// Parent5.call(this);
// this.type = 'child5'
// }
对比 方法4中的实例s5 和 方法5中的实例s7 的原型
s5实例和s7实例的原型对象
发现s5的原型对象引用是其父类的原型链,而s7的原型链上是增加了父类这个对象的,从原型链看继承的角度也说明s7在继承上是比s5合理的。