知识重温:
- 每个构造函数都有一个原型对象,原型有一个属性(constructor)指回构造函数,而实例有一个内部指针_proto_([[Prototype]]) 指向原型。实例与构造函数的原型之间有直接关系,实例与构造函数之间没有。
- 原型链的基本思想:上面如果原型是另一个类型的实例呢?意味着这个原型本身有一个内部指针指向另一个原型,相应的另一个原型也有一个指针(constructor)指向另一个构造函数。这样就在实例和原型之间构造了一条原型链.原型链还有一环,所有引用类型都继承Object,任何函数的默认原型都是一个Object实例,这就意味着这个实例有一个内部指针指向object.prototype.
一.原型链继承
基本思路:通过原型继承多个引用类型的属性和方法。
缺点:
1.原型中包含的引用值会在所有实例间共享
2.子例在实例化时不能给父类型的构造函数传递参数。
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function (){
return this.property;
}
function SubType (){
this.subproperty = false;
}
//继承 SuperType
subType.prototype =new SuperType();
SubType.prototype.getSubValue = function (){
return this.subproperty;
}
let instance = new Subtype();
console.log(instance.getSuperValue()); //true
注意细节:
- 子类有时需要覆盖父类的方法,或者增加父类没有的方法,这些方法必须在原型赋值之后(即subType.prototype =new SuperType() 后)再添加到原型上。
- 以对象字面量方式创建原型方法会破坏之前的原型链,因为这相当于重写了原型链。
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function (){
return this.property;
}
function SubType (){
this.subproperty = false;
}
//继承 SuperType
subType.prototype =new SuperType();
//子类有时需要覆盖父类的方法,或者增加父类没有的方法,这些方法必须在原型赋值之后
//新方法
SubType.prototype.getSubValue = function (){
return this.subproperty;
}
//覆盖已有方法
SubType.prototype.getSuperValue = function (){
return false;
}
let instance = new Dubtype();
console.log(instance.getSuperValue()); //fasle
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function (){
return this.property;
}
function SubType (){
this.subproperty = false;
}
//继承 SuperType
subType.prototype =new SuperType();
//通过对象字面量添加新方法,这会导致上一行无效
//覆盖后原型是一个object实例,不在是superType的实例,其constructor属性也指向了完全不同的新对象(指向object构造函数)
Subtype.prototype= {
getSubValue(){
return this.subproperty;
},
someotherMethod(){
return false;
}
}
let instance = new Dubtype();
console.log(instance.getSuperValue()); //出错!!
二.盗用构造函数
基本思路:在子类构造函数中调用父类构造函数。因为毕竟函数就是在特定上下文中执行代码的简单对象,所以可以使用apply()和call()方法以新创建的对象为上下文执行构造函数。
缺点:
1. 实例并不是父类的实例,只是子类的实例
2. 只能继承父类的实例属性和方法,不能继承原型属性/方法
3. 无法实现函数复用,每个子类都有父类实例函数的副本,影响性能
function SuperType(name){
this.color = ['red','blue','green']
this.name = name
}
function SubType (){
//继承SuperType并传参
SuperType.call(this,'我的名字')
//实例属性
this.age = 29;
}
let instance1 = new subType();
instance1.color.push('black')
console.log(instance1.colors); //red ,blue green ,black
let instance2 = new subType();
console.log(instance2.colors); //red ,blue green
let instance = new subType();
console.log(instance.name) // ‘我的名字’
console.log(instance.age) //29
三.组合继承
基本思路:使用原型链继承原型上的属性和方法(实现复用),而通过盗用构造函数继承实例属性(实现传参且实例相互独立)。
缺点: 创建的实例和原型上存在两份相同的属性
function Parent(name) {
this.name = name;
this.newArr = ["red", "blue", "green"];
}
Parent.prototype.sayName = function() {
console.log(this.name);
};
function Child(name) {
Parent.call(this, name);
this.age = 26;
}
Child.prototype = new Parent();
//重写Child.prototype的constructor属性,使其执行自己的构造函数Child
Child.prototype.constructor = Child;
Child.prototype.sayAge = function() {
console.log(this.age);
};
var Person1 = new Child("heyushuo");
console.log(Person1);
Person1.newArr.push("yellow");
console.log(Person.newArr); //["red", "blue", "green","yellow"]
Person.sayName(); //heyushuo
var Person2 = new Child("kebi");
console.log(Person2.newArr); //["red", "blue", "green"]
Person.sayName(); //kebi
四.原型式继承
基本思路:有一个object函数,这个函数创建一个临时构造函数,将传入的对象赋值给这个构造函数的原型,然后返回这个临时构造函数的一个实例。本质上,object()是对传入对象执行了一次浅复制。
原型式继承适合不需要单独创建构造函数,但仍需要在对象间共享信息的场所。
缺点:属性中包含的引用值始终会在相关对象间共享,跟使用原型链模式一样。
function object(o){
function F(){};
F.prototype = o;
return new F();
}
等价于使用ES5的Object.create();
五.寄生式继承
基本思路:创建一个仅用于封装继承过程的函数,该函数在内部以某种形式来做增强对象,最后返回对象。
缺点:通过寄生式继承给对象 添加函数会导致函数难以重用,与构造函数类似。:跟借用构造函数模式一样,每次创建对象都会创建一遍方法。
function object(o){ //创建一个实现继承的函数 函数功能等价于使用ES5的Object.create();
function F(){};
F.prototype = o;
return new F();
}
function createAnother(original){
let clone = object(original); //通过调用函数创建一个新的对象
clone.sayHi(){ //以某种方式增强这个对象
console.log('hi')
};
return clone ; //返回这个对象
}
let person ={
name:'koo'
}
let anotherPerson = createAnother(person);
anotherPerson.sayHi() //hi
六.寄生式组合继承
基本思路:不是通过调用父类构造函数给子类原型赋值,而是取得父类原型上的一个副本。说到底就是使用寄生式继承来继承父类原型,然后将返回的新对象赋值给子类的原型。
function object(o){ //创建一个实现继承的函数
function F(){};
F.prototype = o;
return new F();
}
function inherPerototype(subtype,supertype){
subtype.prototype = object(supertype.prototype); //subtype = Object.create(supertype.prototype);
subtype.prototype.constructor = supertype;
}
function SuperType(name){
this.color = ['red','blue','green']
this.name = name
}
SuperType.prototype.sayName = function() {
console.log(this.name);
};
function SubType (name){
SuperType.call(this,name,age)
this.age = age;
}
inheritPrototype(subType,superType);
SubType.prototype.sayAge = function (){
console.log(this.age)
}