简介
1.OO语言都支持两种继承方式:借口继承和实现继承。接口继承只继承方法签名,而实现继承则继承实际的方法。
2.ECMAScript无法实现接口继承,只支持实现继承,主要依靠原型链实现。
原型链
原理
利用原型让一个引用类型继承另一个引用类型的属性和方法。
分析
(1)每个构造函数都有一个原型对象
(2)原型对象都包含一个指向构造函数的指针,实例都包含一个指向原型对象的内部指针([[Prototype]])
比如:有一个对象A,则
实例A——>原型对象A
如果原型对象是另一个类型的实例,比如对象B,则
实例A——>原型对象A(实例B)——>原型对象B
此时对象A的原型对象A变为对象B的实例,而实例B的[[Prototype]]指针指向原型对象B,则原型对象A[[Prototype]]指针指向原型对象B
如果原型对象B也是另一类型的实例呢?如此层层递进,就构成了实例与原型的链条
(3)实现原型链的基本模式:
图示:
function SuperType() {
this.property = true;
}
SuperType.prototype.getSuperValue = function () {
return this.property;
};
function SubType() {
this.subproperty = false;
}
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function () {
return this.subproperty;
};
var instance = new SubType();
console.log(instance.getSuperValue());
结果
原型搜索顺序:搜索实例->搜索SubType.prototype->搜索SuperType.prototype->直到原型链末端
(4)实现的本质是重写原型对象,代之以一个新类型的实例
(5)原型搜索机制:当以读取模式访问一个实例属性时,首先会在实例中搜索该属性。如果没有找到,则搜索实例的原型。
默认的原型
所有的引用类型默认都继承了Object,这个继承是通过原型链实现的,这也是所有自定义类型都会继承toString()、valueOf()等方法的根本原因
例如上面的例子,完整的原型链为:
确定原型和实例的关系
instanceof
测试实例与原型链中出现过的构造函数,结果返回true
console.log(instance instanceof Object);
console.log(instance instanceof SuperType);
console.log(instance instanceof SubType);
结果
isPrototypeOf()
只要是原型链中出现过的原型,都是该原型链所派生出来的实例的原型
console.log(Object.prototype.isPrototypeOf(instance));
console.log(SuperType.prototype.isPrototypeOf(instance));
console.log(SubType.prototype.isPrototypeOf(instance));
结果
谨慎地定义方法
1.给原型添加方法的代码一定要放在替换原型的语句之后
例如
function SuperType() {
this.property = true;
}
SuperType.prototype.getSuperValue = function () {
return this.property;
};
function SubType() {
this.subproperty = false;
}
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function () {
return this.subproperty;
};
var instance1 = new SubType();
//重写了.getSuperValue()方法
SubType.prototype.getSuperValue = function () {
return false;
};
var instance2 = new SuperType();
console.log(instance1.getSuperValue());
console.log(instance2.getSuperValue());
如果放在之前
function SuperType() {
this.property = true;
}
SuperType.prototype.getSuperValue = function () {
return this.property;
};
function SubType() {
this.subproperty = false;
}
SubType.prototype.getSuperValue = function () {
return false;
};
SubType.prototype.getSubValue = function () {
return this.subproperty;
};
//原型对象被实例替换掉
SubType.prototype = new SuperType();
var instance1 = new SubType();
console.log(instance1.getSuperValue());//调用的是SuperType的方法
console.log(instance1.getSubValue());//找不到方法
结果
2.通过原型链实现继承时,不能使用对象字面量创建原型方法,这样会重写原型链
例如
function SuperType() {
this.property = true;
}
SuperType.prototype.getSuperValue = function () {
return this.property;
};
function SubType() {
this.subproperty = false;
}
SubType.prototype = new SuperType();
SubType.prototype = {
getSubValue: function () {
return false;
}
};
var instance = new SubType();
console.log(instance.getSuperValue());
结果
问题
1.包含引用类型值的原型属性会被所有实例共享
2.在创建子类型的实例时,不能向超类型的构造函数中传递参数
3.实践很少会单独使用原型链
总结
1.理解原型搜索引擎机制的原理,即沿着原型链搜索直到末端
2.ECMAScript将原型链作为实现继承的主要方法,基本思路是利用原型让一个引用类型继承另一个引用类型的属性和方法
3.通过instanceof和isPrototypeOf()确定原型和实例之间的关系
4.单独使用原型链会使原型属性被所有实例共享,并且创建子类型的实例时,不能向超类型的构造函数传递参数
参考
《JavaScript高级程序设计(第3版)》