一、.原型链:
基本思想:就是利用原型让一个引用类型继承另一个引用类型的属性与方法;
概念:构造函数、原型、实例的关系:每一个构造函数都有一个原型对象,原型对象内部有一个指向构造函数的指针,实例都包含一个指向原型对象的你内部指针。如果,让一个原型对象等于另一个类型的实例,那么,此时的原型对象将包含指向另一个原型对象的指针,另一个原型对象中也包含这指向另一个构造函数的指针,加入另一个原型又是另一个类型的实例,那以此类推,层层递进,就构成了实例与原型的链条。
var Father = function(x){
this.name = ['red','blue','green'];
this.color = 'red';
}
Father.prototype.type = '华为';
Father.prototype.getfathername=function(){
console.log("我是父亲")
};
var Son = function(){
}
Son.prototype = new Father();
var father = new Father();
var son1 = new Son();
var son2= new Son();
father.getfathername();
son1.getfathername();
son1.name.push('black');
son1.color = 'black';
console.log(son1.name , son2.name); //['red','blue','green','black'] , ['red','blue','green','black']
console.log(son1.color , son2.color); //'black red
弊端:
1.来自引用类型值的原型,通过原型实现继承时,原型实际上会变成另一个 类型的实例。于是,原先的实例属性也就顺理成章的变成了现在的原型属性了。
构造函数Father有一个属性,father的每一个实例都会有这个属性,Son用过原型链 继承了Father之后,Son.prototype就变成了Father的一个实例,Son的所有实例也都会 共享这个属性;
Color(基本类型值):上述例子中son1的color值变了之后,son2的color值没有变化, 这是因为,当实例中存在与原型对象上同名的属性时,会自动屏蔽原型对象上的同 名属性,son1.color=‘black’,只是给son1添加一个本地属color,并设置了相关值,所 以打印son1.color时,实际打印的是son1的本地属性,而不是其原型对象上的属性 (和本地属性同名,已经被屏蔽了)。
Name(引用类型值):son1的name值变了之后,son2的name值也变化了。
所以:原型上任何类型的属性值都不会通过实例被重写,但是引用类型的属性值会 通过实例的影响而修改;
2.在创建子类的实例的时候,不能向父类的构造函数中传参数 。
二、借用构造函数
(为解决原型继承带来的问题,继而用借用构造函数)
基本思想:在子类构造函数的内部调用父类构造函数。可以使用apply()和call()在 新创建的对象上执行构造函数;
var Father = function(x){
this.name = ['red','blue','green'];
this.color = 'red';
}
Father.prototype.type = '华为';
Father.prototype.getfathername=function(){
console.log("我是父亲")
};
var Son = function(){
//通过call(),调用了Father
Father.call(this,'向父类传的x参数');
}
var father = new Father();
var son1 = new Son();
var son2= new Son();
son1.name.push('black');
son1.color = 'black';
console.log(son1.name , son2.name); //['red','blue','green','black'] , ['red','blue','green']
console.log(son1.color , son2.color); //'black red
优点:(1)通过使用call和apply方法,实际上是在新创建的Son实例环境下调用了Father构造函数,这样,就会在新Son对象上之心Father()函数中定义的所有对象初始化代码,所以,Son的每个实例都会有自己的name、color属性了;
(2)相对于原型链老说,借用构造函数可以在子类构造函数中向父类构造函数传递参数
弊端:方法都在构造函数中定义,因为函数复用就无从谈起。而且。在父类原型中定义的方法,对子类是不可见的。
三、组合继承
基本思想:把原型链继承与借用构造函数结合到一块,使用原型链实现对构造函数的原型上的属性和方法的继承,借用构造函数实现对实例属性的继承,这样,既通过在原型上定义方法实现了函数复用,又保障每个实例都有他自己的属性。
var Father = function(x){
this.name = ['red','blue','green'];
this.color = 'red';
}
Father.prototype.type = '华为';
Father.prototype.getfathername=function(){
console.log("我是父亲")
};
var Son = function(){
//通过call(),调用了Father
Father.call(this,'向父类传的x参数');
}
var father = new Father();
Son.prototype = new Father();
var son1 = new Son();
var son2= new Son();
son1.name.push('black');
son1.color = 'black';
console.log(son1.name , son2.name); //['red','blue','green','black'] , ['red','blue','green']
console.log(son1.color , son2.color); //'black red
四、通过中转函数,避免二次实例化属性
var Father = function(x){
this.name = ['red','blue','green'];
this.color = 'red';
}
Father.prototype.type = '华为';
Father.prototype.getfathername=function(){
console.log("我是父亲")
};
var Son = function(){
//通过call(),调用了Father
Father.call(this,'向父类传的x参数');
}
var father = new Father();
Son.prototype = Object.create(Father.prototype);
var son1 = new Son();
var son2= new Son();
son1.name.push('black');
son1.color = 'black';
son1.getfathername();
console.log(son1.name , son2.name); //['red','blue','green','black'] , ['red','blue','green']
console.log(son1.color , son2.color); //'black red