1,构造函数继承
function Parent1() {
this.name = "parent1"
}
Parent1.prototype.say = function () {
console.log("hi")
}
function Child1() {
Parent1.call(this);//构造函数实现继承的关键
this.type = "child1"
}
console.log(new Child1().say) //取不到say方法 报错:Uncaught TypeError: (intermediate value).say is not a function
构造函数继承的实现方式:在子类型构造函数中调用父类型构造函数,并通过call修改this指向;
缺点:只能继承构造函数中的属性和方法,无法继承父构造函数原型对象的属性和方法,无复用性可言;
2,原型链继承
function Parent2() {
this.name = "parent2"
this.color = ["blue", "red"]
}
Parent2.prototype.say = function () {
console.log("hi")
}
function Child2() {
this.type = "child2"
}
Child2.prototype = new Parent2();//原型链继承实现的关键
let c1 = new Child2();
let c2 = new Child2();
c1.color.push("yellow")
console.log(c1.color, c2.color) //["blue", "red", "yellow"], ["blue", "red", "yellow"],此时c1.color改变影响了c2.color
原型链继承实现的方式:子构造函数的原型对象等于父构造函数的实例
因为 new Child2().proto === Child2.prototype;
所以 new Child2().proto = new Parent2();
即子构造函数的原型链指向父构造函数的实例;
缺点:因为所有的子构造函数的原型链都指向父类构造函数的实例,如果父类构造函数的中的属性包含引用类型,这个属性会被所有的子类实例所共享(此时c1.color改变影响了c2.color),因此不推荐使用纯原型链的形式继承
3,组合式继承
function Parent3() {
this.name = "parent3"
this.color = ["blue", "red"]
}
Parent3.prototype.say = function () {
console.log("hi")
}
function Child3() {
Parent3.call(this);//组合继承实现的关键
this.type = "child3"
}
Child3.prototype = new Parent3();//组合继承实现的关键
let c1 = new Child3();
let c2 = new Child3();
childTest1.color.push("yellow");
console.log(c1.color, c2.color, c1.say) //["blue", "red", "yellow"] ,["blue", "red"],
console.log(c1) //结果如下图
组合继承后继同时承了父构造函数中和父构造函数原型中的属性、方法,同时也保证了子类型构造函数属性的独立
组合继承的实现方式:在子类型构造函数中调用父类型构造函数+子构造函数的原型对象等于父构造函数实例
缺点:子类的实例在生成时,多次调用了父类构造函数,如下图
4,组合继承的优化1
function Parent4() {
this.name = "parent4";
this.color = ["blue", "red"]
}
Parent4.prototype.say = function () {
console.log("hi")
}
function Child4() {
Parent4.call(this);//组合继承实现的关键
this.type = "child4"
}
Child4.prototype = Parent4.prototype;//组合继承优化
let c1 = new Child4();
let c2 = new Child4();
c1.color.push('yellow');
console.log(c1.color, c2.color)
为了解决组合继承存在的问题,对组合继承进行优化;把父类构造函数的原型对象赋值给子构造函数的原型对象;此时实例.__proto__的值中只有say方法,没有再次调用父类构造函数自身中的属性和方法;
为了对比,打印下组合继承和优化后的组合继承的实例,如下图
优化后的组合继承实现方法:在子类型构造函数中调用父类型构造函数+子构造函数的原型对象等于父构造函数的原型对象;
缺点:虽然已经可以实现继承,但是此时打印实例的构造函数发现,实例的构造函数是Parent4,而不是Child4,因此需要对组合继承继续优化
//ƒ Parent4() {
// this.name = "parent4";
// this.color = ["blue", "red"]
//}
5.组合继承优化2
function Parent5() {
this.name = "parent5";
this.color = ["blue", "red"]
}
Parent5.prototype.say = function () {
console.log("hi")
}
function Child5() {
Parent5.call(this);//组合继承实现的关键
this.type = "child5"
}
Child5.prototype = Parent5.prototype;//组合继承实现的关键
Child5.prototype.constructor = Child5;//组合继承优化
let c1 = new Child5();
let c2 = new Child5();
console.log(c1.constructor) //Child5
console.log(Parent5.prototype.constructor) //Child5
通过优化,可以发现子类的实例化对象的构造函数指向已经正确,但是,此时打印父构造函数的原型对象的构造器,发现,构造器的指向也是Child5,还要继续优化
6,组合继承优化3
function Parent6() {
this.name = "parent6";
this.color = ["blue", "red"]
}
Parent6.prototype.say = function () {
console.log("hi")
}
function Child6() {
Parent6.call(this);//组合继承实现关键
this.type = "child6"
}
Child6.prototype = Object.create(Parent6.prototype);//组合继承实现关键
Child6.prototype.constructor = Child6;//组合继承实现关键
let c1 = new Child6();
let c2 = new Child6();
c1.color.push('yellow');
console.log(c1.constructor)
console.log(Parent6.prototype.constructor)
优化内容:Object.create(a),可以创建一个原型对象为a的对象,通过Object.create(Parent6.prototype)可以拿到一个和父类原型对象值一样的新对象,在修改子类原型对象的构造器(constructor)时,不会影响到父类对象的构造函数。
致此,已经得到一个较完善的ES5继承~