- 原型链
- 别忘记默认原型
- 确定原型和实例的关系
- 谨慎的定义方法
- 不能使用字面量添加新方法
- 原型链的问题
- 借用构造函数
- 传递参数
- 借用构造函数的问题
- 组合继承
- 原型式继承
- 寄生式继承
- 寄生组合式继承
原型链
/**
* 继承
* 许多OO语音都支持两种继承方式: 接口继承和实现继承
* 1.接口继承: 只继承方法签名 不继承实现
* 2.实现继承:继承实际的方法
*
* 如前所述,由于函数没有签名,在ECMAScript中无法实现接口继承。
* ECMAScript只支持实现继承,而且其实现继承主要是依靠原型链来实现的。
*
*/
/**
* 原型链
* 原型链作为实现继承的主要方法,基本思想是:利用原型让 引用类型A 继承引用类型B 的属性方法。
* 简单回顾构造函数、 原型和实例的关系:
* 1.每个构造函数都有一个原型对象
* 2.原型对象都包含一个指向构造函数的指针
* 3.实例都包含一个指向原型对象的内部指针
* A构造函数 A原型对象 A实例
* B构造函数 B原型对象 B实例
*
* 让A原型对象等于B类型的实例
* A原型对象将包含一个指向B原型的指针
* B原型对象中也包含着一个指B构造函数的的指针
*
*
*
*
*/
function SuperType(){
this.superProperty=true;
}
SuperType.prototype.getSuperValue=function(){
return this.superProperty;
};
function SubType(){
this.subproperty=false;
}
//让A原型对象等于B类型的实例
//继承了SuperType
SubType.prototype=new SuperType();
SubType.prototype.getSubValue=function(){
return this.subproperty;
};
var instance=new SubType();
console.log(instance.getSuperValue())
console.log(instance.getSubValue())
/*
* 上面的代码定义了SuperType 和SubType。 SubType继承了SuperType
* 而继承是通过创建SuperType的实例,赋值给SubType.prototype实现的
* 实现的本质是: 用一个新类型的实例去重写原型对象(原来纯在与SUperType的实例中的所有属性和方法,
* 现在也存在与SubType.prototype 中)
* 确定继承关系后又添加了getSubValue 这个方法。
*
* 上面代码中 SubType 没有使用默认原型,而是换了一个新原型(SuperType实例),
* 新原型有SuperType实例的属性和方法 还有一个指向SuperType原型的指针
*
* 最终结果
*
* 1.instance 指向SubType原型对象
* 2.SubType原型对象指向SuperType 原型对象
* 3.getSuperValue()方法仍在SuperType原型对象中(因为是原型方法)
* 4.proterty在SubType.prototype中(因为是实例属性@see 5)
* 5.SubType.prototype是SuperType的实例
* 6.instance.constructor指向SuperType(constructor被重写)
*/
/**
* 通过实现原型链,本质上扩展了前面介绍的原型搜索机制。
*
* 原型搜索机制:当以读取模式访问一个实例属性时,首先会
* 在实例中搜索该属性。如果没有找到该属性,则继续搜索
* 实例的原型。
*
* 在通过原型链实现继承的情况下,搜索过程就得以沿着原型链
* 继续向上。例如:instance.getSuperValue()会经历三个
* 搜索步骤
* 1:搜索实例
* 2:搜索SubType.prototype;
* 3:搜索SuperType.prototype
* 在找不到属性或方法时,会一直向上搜索,直到原型链末端才会
* 停下 。
*
*/
别忘记默认原型
确定原型和实例的关系
谨慎的定义方法
//必须先用SuperType实例 替换SubType原型后 再定义方法(应为SubType原型被替换,将会找不到替换之前的方法)
/**
* 谨慎的定义方法
* 子类型有时需要重写父类型中的某个方法,或者添加父类型中没有
* 的方法。但是给原型添加方法的代码一定要放在替换原型的语句之后
*/
function SuperType() {
this.property = true;
}
SuperType.prototype.getSuperValue = function() {
return this.property;
}
function SubType() {
this.subproperty = false;
}
//继承了SuperType
SubType.prototype = new SuperType();
//必须先用SuperType实例 替换SubType原型后 再定义方法
SubType.prototype.getSubValue = function() {
return this.subproperty;
};
SubType.prototype.getSuperValue = function() {
return false;
}
var instance =new SubType();
console.log(instance.getSuperValue());//false
不能使用字面量添加新方法
在通过原型链实现继承时,不能使用对象字面量创建原型方法(因为会重写原型链,)
原型链的问题
1.父类型的实例属性,变成子类型的原型属性
/**
* 1.父类型的实例属性,变成子类型的原型属性
*/
function SuperType(){
this.colors=['red','blue','green'];
}
function SubType(){
}
//继承SuperType
SubType.prototype=new SuperType();
var instance1=new SubType();
instance1.colors.push('black');
console.log(instance1.colors);//'red,blue,green,black'
var instance2=new SubType();
console.log(instance2.colors);//'red,blue,green,black'
2.创建子类型时 ,没有办法在不影响所有对象实例的情况下,向父类型构造函数传递参数。
由于这两个问题,实践中很少会单独使用原型链
借用构造函数
借用构造函数:子类型构造函数的内部,调用父类型构造函数。
函数只过不是在特定环境中执行代码的对象,因此通过使用apply()和call()
方法可以在(未来将要)新创建的对象上执行构造函数(代码)
apply() 和call()详见 https://blog.csdn.net/csdn1125550225/article/details/80067801
作用域的问题详见 https://blog.csdn.net/csdn1125550225/article/details/80139005
function SuperType(){
this.colors=['red','blue','green'];
}
function SubType(){
/**
* 继承SuperType。
* 原理 :在SubType函数的作用域调用call(),设置SubType函数体内this
* 对象的值(在SubType对象上执行SuperType()函数的所有对象初始化代码)。
* 使SubType每个实例都拥有colors。
*/
SuperType.call(this)//(相当执行了,this.colors=['red','blue','green'];)
}
var instance1=new SubType();
instance1.colors.push('black');
console.log(instance1.colors)//"red,blue,green,black"
var instance2=new SubType();
console.log(instance2.colors)//"red,blue,green"
SubType的每个实例都拥有了自己的colors
传递参数
/**
* 传递参数:可以在子类型构造函数中向父类型构造函数传递参数
*/
function SuperType(name) {
this.name = name;
}
function SubType() {
//继承了SuperType,同时传递了参数
SuperType.call(this,'Nicholas');
//实例属性 为了确保SuperType构造函数不会重写子类的属性,可以在调用父类之后在定义属性
this.age=29;
}
var instance=new SubType();
console.log(instance.name);//Nicholas
console.log(instance.age);//29
借用构造函数的问题
借用构造函数的问题:属性和方法的复用问题,
方法都在构造函数中定义,原型中没有公共的属性和方法。
在父类型的原型中定义的方法,对子类型是不可见的,除非父类型的构造函数中定义,
但这样所有的类型都只能使用构造函数。所以 借用构造函数也是很少单独使用的
组合继承
组合继承:将原型链和借用构造函数组合到一块,发挥各自的长处的一种继承模式。
思路:使用原型链实现对原型属性和方法的继承,通过借用构造函数实现对实例属性的继承。
好处:通过在原型上定义方法实现函数复用,又保证每个实例都有自己的属性
function SuperType(name){
//定义了两个属性
this.name=name;
this.colors=['red','blue','green'];
}
//给SuperType原型定义方法
SuperType.prototype.sayName=function () {
console.log(this.name)
}
function SubType (name,age) {
//借用构造函数 继承属性
SuperType.call(this,name);
//定义自己的属性
this.age=age;
}
//SubType通过原型链继承SuperType的方法
SubType.prototype=new SuperType();
//在新的原型上定义方法
SubType.prototype.sayAge=function () {
console.log(this.age)
}
var instance1=new SubType('Nicholas',29);
instance1.colors.push('black');
console.log(instance1.colors)//"red,blue,green,black"
instance1.sayName()//"Nicholas"
instance1.sayAge();//29
var instance2=new SubType('Greg',27);
console.log(instance2.colors)//"red,blue,green"
instance2.sayName()//"Greg"
instance2.sayAge();//27
总结
原型式继承
原型式继承:借助原型可以基于已有的对象创建新对象, 同时不必因此创建自定义类型。