</pre><span style="font-family: Helvetica, 'Hiragino Sans GB', 微软雅黑, 'Microsoft YaHei UI', SimSun, SimHei, arial, sans-serif; font-size: 14.7368px; line-height: 25.2632px; widows: auto;">继承是OO语言中的一大特点,许多OO语言都支持两种方式的继承:接口继承和实现继承。ECMAScript只支持实现继承,主要是依靠原型链来实现的。</span><div style="margin:0px; font-family:Helvetica,'Hiragino Sans GB',微软雅黑,'Microsoft YaHei UI',SimSun,SimHei,arial,sans-serif; font-size:14.7368px; line-height:25.2632px; widows:auto"> 原型链</div><div style="margin:0px; font-family:Helvetica,'Hiragino Sans GB',微软雅黑,'Microsoft YaHei UI',SimSun,SimHei,arial,sans-serif; font-size:14.7368px; line-height:25.2632px; widows:auto"> <span style="line-height:1.6"> |--实现原型链的基本模式:</span></div><div style="margin:0px; font-family:Helvetica,'Hiragino Sans GB',微软雅黑,'Microsoft YaHei UI',SimSun,SimHei,arial,sans-serif; font-size:14.7368px; line-height:25.2632px; widows:auto"><pre name="code" class="javascript">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;
}
var instance = new SubType();
alert(instance.getSuperValue());//true
|--确定原型链和实例的关系
|--instanceof:只要用这个操作符来测试实例与原型链中出现过的构造函数,结果就会返回true。
|--isPrototypeOf():只要是在原型链中出现的原型都可以说是该原型链所派生的实例原型,因此也返回true
|--谨慎的定义方法
|--子类型重写超类型的方法会屏蔽超类型的同名方法,子类的实例调用该方法时都是子类型的该方法,父类的实例则仍是调用父类的同名方法。
|--原型链的问题
|--包含引用类型值的原型属性会被所有的实例共享;这也正是为什么要在构造函数中,而不是在原型对象中定义属性的原因。
|--在创建子类型的实例时,不能向超类型的构造函数中传递参数。
|--借用构造函数(constructor stealing或这叫伪造对象或经典继承)
|--即在子类型构造函数的内部调用超类型构造函数。可以通过apply()和call()方法在新创建的对象上执行构造函数。
|--借用构造函数的问题
|--如果仅使用借用构造函数,无法避免方法都在构造函数中定义,因此函数复用就无从谈起。而且超类中定义的方法对子类而言也是不可见的,结果所有类型都只能使用构造函数模式。
function SuperType(){
this.colors = ["red","blue","green"];
}
function SubType(){
SuperType.call(this);
}
var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors);
var instance2 = new SubType();
alert(instance2.colors);
|--组合继承(combination inheritance)
|--组合式继承避免了原型链和借用构造函数的缺陷,融合了它们的优点,是JavaScript中最常用的继承模式。
|--
function SuperType(name){
this.name = name;
this.colors = ["red","blue","green"];
}
SuperType.prototype.sayName = function(){
alert(this.name);
}
function SubType(name,age){
SuperType.call(this,name);//第二次调用SuperType()
this.age = age;
}
SubType.prototype = SuperType();//第一次调用SuperType()
SubType.prototype.sayAge = function(){
alert(this.age);
}
|--原型式继承(道格拉斯·克罗克福德)
|--这种方式没有使用严格意义上的构造函数。他的想法是借助原型可以基于已有的对象创建新对象,同时还不必因此创建自定义类型。
|--要求:必须有一个对象可以作为另一个对象的基础。
|--例子:
var person = {
name = "test",
friends = ["a","b","c"]
}
var anotherPerson = object(person);
|--ECMAScript5通过新增Object.create()方法规范原型式继承。此方法的第二个参数格式与Object.defineProperties()方法的第二个参数格式相同。
var person = {
name = "test",
friends = []
};
var anotherPerson = Object.create(person,{
name:{
value:"other"
}
});
|--寄生式继承(parasitic)
|--与寄生构造函数和工厂模式类似,即创建一个仅用于封装继承过程的函数,在函数内部增强对象,并返回这个对象。
function createAnother(original){
var clone = object(original);
clone.sayHi = function(){
alert("hi");
}
return clone;
}
var person = {};
var another = createAnother(person);
|--寄生组合式继承
|--借用构造函数来继承属性,通过原型链的混成形式来继承方法。思路:不必为了指定子类型的原型而调用超类型的构造函数,因为我们只需要父类型的一个副本而已,本质上就是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型。
</pre><pre name="code" class="javascript">function inheritPrototype(subType,superType){
var prototype = object(superType.prototype);
prototype.constructor = subType;
subType.prototype = prototype;
}<pre name="code" class="javascript">function SuperType(name){
this.name = name;
this.colors = ["red"];
}
SuperType.prototype.sayName = function(){
alert(this.name);
}
function SubType(name,age){
SuperType.call(this,name);
this.age = age;
}
inheritPrototype(SubType,SuperType);
寄生组合式继承只调用了一次SuperType构造函数,并且避免了在SubType的prototype上创建多余的属性,同时保持了原型链不变,能够正常使用instanceof()和isPrototypeOf(),是目前最理想的继承范式。