原型链继承
原型链继承-通过构造函数的实例对象继承
<script>
function GaoBW(){
this.car = '兰博基尼';
this.house = '温哥华山庄别墅1001号';
}
function Father(n,a){
this.name = n;
this.age = a;
}
Father.prototype = new GaoBW();
var tc_ = new Father('铁锤',10);
console.log(tc_.name,tc_.car,tc_.house);//铁锤 兰博基尼 温哥华山庄别墅1001号
</script>
这种继承方式的弊端是:当前函数的原型对象和实例对象的constructor指针会指向被继承函数
造成继承链紊乱
constructor属性默认指向当前构造函数,但是当进行 Father.prototype = new GaoBW();操作时;
当前函数的显性原型prototype属性(是一个对象)的constructor指针就指向 构造函数 GaoBW()
而实例对象通过_ _proto_ _属性(也叫隐式原型,也是一个对象)继承 Father.prototype的内容,所以他的constructor指针也指向构造函数 GaoBW();
console.log(tc_.constructor); //结果是GaoBW,
解决方式:Father.prototype.constructor=Father;手动修改继承链。
原型链继承-通过构造函数prototype属性继承
<script>
function GaoBW(){}
GaoBW.prototype.house = '温哥华山庄别墅1001号';
GaoBW.prototype.car = '兰博基尼';
function Father(n,a){
this.name = n;
this.age = a;
}
Father.prototype = GaoBW.prototype;
//手动修改继承链
Father.prototype.constructor = Father;
var tc_ = new Father('tc_',20);
console.log(tc_.name,tc_.house,tc_.car);
console.log(tc_.constructor);
</script>
这种方式的
优点:效率比较高(不用执行和建GaoBw的实例了)
缺点:1、依然会产型继承链紊乱,需要修改继承链
2、因为father的原型对象指向了GaoBw的原型对象,所以father修改原型对象的值,高百万原型对象的值也被修改了,这样就等于继承了别人的财富,还要随意的更改别人的内容,太霸道,不合理
Father.prototype = GaoBW.prototype;
//手动修改继承链
//Father.prototype.constructor = Father;
var tc_ = new Father('tc_',20);
console.log(tc_.name,tc_.house,tc_.car);
console.log(tc_.constructor);
// 另一个弊端
Father.prototype.car = '五菱宏光';
var gbw_ = new GaoBW();
console.log(gbw_.car);//五菱宏光
因为GaoBW.prototype;属于引用数据类型当他赋值的时候不是直接传递的数据内容,而是在栈中的地址(用16进制表示),该地址指向堆中的内容。
所以当 Father.prototype的内容修改时,GaoBW.prototype的内容也会发生变化;
引入空对象继承
为了解决当前原型影响被继承函数原型的问题,我们可以引入一个空的对象作为一个中转站
<script>
function GaoBW(){}
GaoBW.prototype.car = '兰博基尼';
GaoBW.prototype.house = '温哥华山庄别墅1001号';
function F(){}
F.prototype= GaoBW.prototype;
function Fater(n,a){
this.a=a;
this.n=n;
}
Fater.prototype = new F();
Fater.prototype.constructor=Fater;
var tc_ = new Fater('tc',20);
console.log(tc_.car,tc_.house)//'兰博基尼','温哥华山庄别墅1001号'
Fater.prototype.car = '五菱';
var gao_ = new GaoBW();
console.log(gao_.car);//兰博基尼
</script>
构造函数继承
call方法&&apply方法
call:
语法:
a.call(b,ag1,ag2);
表示b继承a,ag1,ag2是a的参数
apply:
语法:
a.apply(b,[ag1,ag2]);
表示b继承a,ag1,ag2是a的参数
<script>
function Father(house,car){
this.car_=car;
this.house_ = house;
}
function Son(n,age){
this.n_=n;
this.age_ = age;
//son 继承 father
//a.call(b,ag1,ag2);
//表示b继承a,ag1,ag2是a的参数
Father.call(this,'汤臣一品','大众捷达');
//Father.apply(this,['汤臣一品','大众捷达']);
}
var son_ = new Son('小宝',20);
console.log(son_.house_);//汤臣一品
console.log(son_.car_);//大众捷达
</script>
call和apply继承主要通过传参的方式实现,两者的区别就是apply继承的参数是数组的形式
但是两者都无法访问被继承函数原型对象里的内容
Father.prototype.hobby='sing'
console.log(son_.hobby);//undefined
伪经典继承(组合继承)
将原型链继承和构造函数继承组合在一块
原型链实现对原型属性和方法的继承
借用构造函数实现对实例属性的继承
<script>
function Father(house, car) {
this.car_ = car;
this.house_ = house;
}
Father.prototype.say = function(){
console.log('我是祖宗');
}
function Son(n,a){
this.name = n;
this.age = a;
// Father.call(this,'汤臣一品','劳斯莱斯');
Father.apply(this,['汤臣一品','哈罗单车']);
}
Son.prototype = new Father();//
Son.prototype.constructor = Son;//手动更改原型对象
var xw = new Son('小王',30);
console.log(xw.car_,xw.house_,xw.name,xw.age);
console.log(xw.constructor);// Son
xw.say();// 我是祖宗
</script>