第26章 继承

1 几种继承方式

1.1 原型链

        将原型链作为实现继承的主要方法。其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。
        假如我们让原型对象等于另一个类型的实例,另一个原型又是另一个类型的实例,就构成了 实例与原型的链条。这就是所谓 原型链的基本概念。
        原型链的构建是通过将一个类型的 例赋值给另一个构造函数的 原型实现的。这样,子类型就能够访问 超类型的所有属性和方法

1.1.1概念 

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; 
 }, 
 someOtherMethod : function (){ 
 return false; 
 } 
};
//添加新方法
SubType.prototype.getSubValue = function (){ 
 return this.subproperty; 
}; 
//重写超类型中的方法
SubType.prototype.getSuperValue = function (){ 
 return false; 
};
var instance = new SubType(); 
alert(instance.getSuperValue()); //false

继承:实现的本质是重写原型对象,代之以一个新类型的实例。换句话说,原来存在于 SuperType 的实例中的所有属性和方法,现在也存在于 SubType.prototype 中了。

添加方法:给原型添加方法的代码一定要放在替换原型的语句之后。不能使用对象字面量创建原型方法。因为这样做就会重写原型链。

重写方法 :但重写这个方法将会屏蔽原来的那个方法。换句话说,当通过 SubType 的实例调用 getSuperValue() 时,调用的就是这个重新定义的方法;但通过 SuperType 的实例调用 getSuperValue() 时,还会继续调用原来的那个方法。
读取 :当以读取模式访问一个实例属性时,搜索过程就得以沿着原型链继续向上。instance.getSuperValue()会经历三个搜索步骤:1)搜索实例;2)搜索 SubType.prototype;3)搜索 SuperType.prototype,最后一步才会找到该方法。
继承 :子类的原型等于超类的实例。SubType原型其内部还有一个指针,指向了 SuperType 的原型
默认原型 :所有引用类型默认都继承了 Object,而这个继承也是通过原型链实现的。所有函数的默认原型都是 Object 的实例,因此默认原型都会包含一个内部指针, 指向 Object.prototype。这也正是所有自定义类型都会继承 toString()、valueOf()等默认方法的根本原因。
超类 :getSuperValue() 方法仍然还在SuperType.prototype 中,但 property 则位于 SubType.prototype 中。这是因为 property 是一个实例属性,而 getSuperValue() 则是一个原型方法

1.1.2. 确定原型和实例的关系

  • 第一种方式是使用 instanceof 操作符,只要用这个操作符来测试实例与原型链中出现过的构造函数,结果就会返回 true
alert(instance instanceof Object); //true 
alert(instance instanceof SuperType); //true 
alert(instance instanceof SubType); //true
  • 第二种方式是使用 isPrototypeOf()方法。同样,只要是原型链中出现过的原型,都可以说是该原型链所派生的实例的原型。
alert(Object.prototype.isPrototypeOf(instance)); //true 
alert(SuperType.prototype.isPrototypeOf(instance)); //true 
alert(SubType.prototype.isPrototypeOf(instance)); //true

1.1.3原型链的问题

  • 最主要的问题来自包含引用类型值的原型,对象实例共享所有继承的属性和方法SubType 通过原型链继承了SuperType 之后,SubType.prototype 就变成了 SuperType 的一个实例。结果是 SubType 的所有实例都会共享这一个 colors 属性
  • 在创建子类型的实例时,不能向超类型的构造函数中传递参数。
function SuperType(){ 
 this.colors = ["red", "blue", "green"];
} 
function SubType(){ 
} 
//继承了 SuperType 
SubType.prototype = new SuperType(); 
var instance1 = new SubType(); 
instance1.colors.push("black"); 
alert(instance1.colors); //"red,blue,green,black" 
var instance2 = new SubType(); 
alert(instance2.colors); //"red,blue,green,black"

1.2 借用构造函数

1.2.1概念

        在子类型构造函数的内部调用超类型构造函数。这样就可以做到每个实例都具有自己的 属性,同时还能保证只使用构造函数模式来定义类型。

function SuperType(){
 this.colors = ["red", "blue", "green"]; 
} 
function SubType(){ 
 //继承了 SuperType 
 SuperType.call(this); 
} 
var instance1 = new SubType(); 
instance1.colors.push("black"); 
alert(instance1.colors); //"red,blue,green,black" 
var instance2 = new SubType(); 
alert(instance2.colors); //"red,blue,green"

我们实际上是在(未来将要)新创建的 SubType 实例的环境下调用了 SuperType 构造函数。SubType 的每个实例就都会具有自己的 colors 属性的副本了。

可以在子类型构造函数中向超类型构造函数传递参数
function SuperType(name){ 
 this.name = name; 
} 
function SubType(){ 
 //继承了 SuperType,同时还传递了参数
 SuperType.call(this, "Nicholas"); 
 
 //实例属性
 this.age = 29; 
} 
var instance = new SubType(); 
alert(instance.name); //"Nicholas"; 
alert(instance.age); //29
超类型属性 :SuperType 只接受一个参数 name ,实际上是为 SubType 的实例设置了 name 属性
子类型属性 :为了确保 SuperType 构造函数不会重写子类型的属性,可以在调用超类型构造函数后,再添加应该在子类型中定义的属性

1.2.2 问题

  • 方法都在构造函数中定义,因此函数复用就无从谈起了。
  • 超类型的原型中定义的方法,对子类型而言也是不可见的。

1.3 组合继承

1.3.1概念

        思路是使用原型链继承共享的属性和方法,而通过借用构造函数继承实例属性。成为 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 = new SuperType(); //第一次调用 SuperType()
SubType.prototype.constructor = SubType; 
SubType.prototype.sayAge = function(){ 
 alert(this.age); 
}; 

var instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black"); 
alert(instance1.colors); //"red,blue,green,black" 
instance1.sayName(); //"Nicholas"; 
instance1.sayAge(); //29 

var instance2 = new SubType("Greg", 27); 
alert(instance2.colors); //"red,blue,green" 
instance2.sayName(); //"Greg"; 
instance2.sayAge(); //27

让两个不同的 SubType 实例既分别拥有自己属性——包括 colors 属性,又可以使用相同的方法了。

1.3.2问题

调用两次超类型构造函数,有两组 name colors 属性:一组在实例上,一组在 SubType 原型中。
  • 一次是在创建子类型原型的时候:SubType.prototype 会得到两个属性:name colors;它们都是 SuperType 的实例属性(原型属性
  • 另一次是在子类型构造函数内部:又在新对象上创建了实例属性 name 和 colors。于是,这两个属性就屏蔽了原型中的两个同名属性。(实例属性

1.4 寄生组合式继承

        通过借用构造函数来继承属性,通过原型链的混成形式来继承方法
        本质上,就是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型。开发人员普遍认为寄生组合式继承是引用类型最理想的继承范式。
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); 
 
 this.age = age; 
} 
inheritPrototype(SubType, SuperType); 
SubType.prototype.sayAge = function(){ 
 alert(this.age); 
};

function inheritPrototype(subType, superType){ 
 var prototype = object(superType.prototype); //创建对象
 prototype.constructor = subType; //增强对象
 subType.prototype = prototype; //指定对象
}
第一步是创建超类型原型的一个副本
第二 步是为创建的副本添加 constructor 属性,从而弥补因重写原型而失去的默认的 constructor 属性。
最后一步,将新创建的对象(即副本)赋值给子类型的原型

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值