JavaScript:原型链、继承

标签: 面向对象继承原型链JavaScript继承寄生式继承
315人阅读 评论(0) 收藏 举报
分类:

1、理解原型对象

    1).只要创建了一个新函数,就会为该函数创建一个prototype属性,这个属性指向函数的原型对象;

    2).所有原型对象都会自动获得一个constructor(构造函数)属性,这个属性包含一个指向prototype属性所在函数的指针;

    3).当调用构造函数创建一个新实例后,该实例的内部将包含一个指针(内部属性),指向构造函数的原型对象。

    创建对象时,我们应该在构造函数内定义一般成员,而是其原型定义成员函数。

   

2、原型链

   综上所述,假如我们让原型对象等于另一个类型的实例,则此时的原型对象将包含一个指向另一个原型的实例,相应地,另一个原型中也包含着一个指向另一个构造函数的指针。假如另一个原型又是另一个类型的实例,那么上述关系依然成立,如此层层递进,就构成了实例与原型的链条,这就是原型链。

   【注】:所有引用类型默认都继承了Object,而这个继承也是通过原型链实现的。所有函数的默认原型都是Object的实例,因此默认原型都会包含一个内部指针,指向Object.prototype。这也正是所有自定义类型都会继承toString(),valueOf()等默认方法的根本原因。

   JS中有两个特殊的对象:Object与Function,它们都是构造函数,用于生成对象。

   Object.prototype是所有对象的祖先,Function.prototype是所有函数的原型,包括构造函数。

   可以将JS的对象分为三类:用户创建对象,构造函数对象,原型对象。

   所有对象中都有一个__proto__属性,其指向此对象的原型。

   构造函数对象有prototype,指向其原型对象,通过此构造函数创建对象时,新创建对象的__proto__属性将会指向构造函数的prototype属性。

   原型对象有一个constructor属性,指向它对应的构造函数。

3、继承

(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 instance=new SubType();
alert(instance.getSuperValue());//true

这个例子中的实例以及构造函数和原型之间的关系如图:


确定原型和实例的关系有两种方式:第一种方式使用instanceof操作符;例如:

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

  注意:不能使用对象字面量创建原型方法,因为这样会重写原型链。

  缺点:最主要的问题是包含引用类型值的原型;二是在创建子类型的实例时,不能向超类型的构造函数中传递参数。


(2)、借用构造函数

  在子类型构造函数的内部调用超类型构造函数。使用apply()和call()方法,可以向超类型的构造函数中传递参数。apply()和call()的作用一样,只是专递的参数形式不同,apply()的参数是以数组的形式传递,call()中是展开的形式。

function SuperType(name){
this.name=name;
this.colors=["red","blue","green"];
}
function SubType(){
SuperType.call(this,"Nicholas");//继承了SuperType
this.age=20;
}
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"
alert(instance2.name);//"Nicholas"
alert(instance2.age);//20

  优点:借用构造函数有一个很大的优势,即可以在子类型构造函数中向超类型构造函数传递参数。

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


(3)、组合继承(原型链+借用构造函数)

  其背后的思路是使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。这样,即通过在原型上定义方法实现了函数复用,又能保证每个实例都有它自己的属性。

function SuperType(name){
this.name=name;
this.colors=["red","blue","green"];
}
SuperType.prototype.sayName=function(){
return this.name;   
}
function SubType(name,age){
SuperType.call(this,name);//继承了SuperType
this.age=age;
}
SubType.prototype=new SuperType();
SubType.prototype.constructor=SubType;
SubType.prototype.sayAge=function(){
return this.age;
}
var instance1=new SubType("Nicholas",20);
instance1.colors.push("black");
alert(instance1.colors);//"red,blue,green,black"
alert(instance1.sayName());//"Nicholas"
alert(instance1.sayAge());//20
var instance2=new SubType("Greg",27);
alert(instance2.colors);//"red,blue,green"
alert(instance2.sayName());//"Greg"
alert(instance2.sayAge());//27

使用Object.create()方法实现继承:

function Person(name,age){
	this.name=name;
	this.age=age;
}
Person.prototype.hi=function(){
	alert("Hi,my name is "+this.name+",I'm "+
	this.age+" years old now,");
}
Person.prototype.LEGS_NUM=2;
Person.prototype.ARMS_NUM=2;
Person.prototype.walk=function(){
	alert(this.name+" is walking...");
}
function Student(name,age,className){
	Person.call(this,name,age);
	this.className=className;
}
Student.prototype=Object.create(Person.prototype);
Student.prototype.constructor=Student;
Student.prototype.hi=function(){
	alert("Hi,my name is "+this.name+",I'm "+
	this.age+" years old now,and from "+this.className+",");
}
Student.prototype.learn=function(subject){
	alert(this.name+" is learning "+subject+" at "+
	this.className+",");
}
var bosn=new Student('Bosn',27,'Class3,Grade 2');
bosn.hi();//Hi,my name is Bosn,I'm 27 years old now,and from Class3,Grade 2'
alert(bosn.LEGS_NUM);//2
bosn.walk();//Bosn is walking..
bosn.learn('math');//Bosn is learning matn at Class3,Grade 2


避免了(1)、(2)的缺陷,融合了他们的优点,成为javascript最常用的继承模式,而且,instanceof和isPrototypeOf()也能够用于识别基于组合继承创建的对象。


(4)、原型式继承

  这种方法并没有使用严格意义上的构造函数。是借助原型可以基于已有的对象创建新的对象,同时还不必因此创建自定义类型。

function object(o){
  function F(){}
  F.prototype=o;
  return new F(); 
}
    在object()函数内部,先创建一个临时性的构造函数,然后将传入的对象作为这个构造函数的原型,最后返回了这个临时类型的一个新实例。从本质上讲,object()对传入其中的对象执行了一次浅复制。

function object(o){
  function F(){}
  F.prototype=o;
  return new F(); 
}
var person={
	name:"Nicholas",
	friends:["Shelby","Court","van"]
}
var anotherPerson=object(person);
anotherPerson.name="Greg";
anotherPerson.school="kunming";
anotherPerson.friends.push("Rob");
var yetAnotherPerson=object(person);
yetAnotherPerson.name="Linda";
yetAnotherPerson.friends.push("Barbie");
alert(person.friends);//"Shelby,Court,van,Rob,Barbie"
alert(person.name);//"Nicholas"
alert(anotherPerson.friends);//"Shelby,Court,van,Rob,Barbie"
alert(anotherPerson.name);//"Greg"
alert(anotherPerson.school);//"kunming"
alert(yetAnotherPerson.friends);//"Shelby,Court,van,Rob,Barbie"
alert(yetAnotherPerson.name);//"Linda"

  原型式继承要求你必须有一个对象可以作为另一个对象的基础。如果有这么一个对象的话,可以把它传递给object()函数,然后根据具体需求对得到的对象加以修改即可。
  ECMAScript5通过新增Object.create()方法规范化了原型式继承。这个方法接收两个参数:一个用作新对象原型的对象和(可选的)一个为新对象定义额外属性的对象。在传入一个参数的情况下,Object.create()与object()方法的行为相同。

var person={
	name:"Nicholas",
	friends:["Shelby","Court","van"]
}
var anotherPerson=Object.create(person);
anotherPerson.name="Greg";
anotherPerson.school="kunming";
anotherPerson.friends.push("Rob");
var yetAnotherPerson=Object.create(person);
yetAnotherPerson.name="Linda";
yetAnotherPerson.friends.push("Barbie");
alert(person.friends);//"Shelby,Court,van,Rob,Barbie"
alert(person.name);//"Nicholas"
alert(anotherPerson.friends);//"Shelby,Court,van,Rob,Barbie"
alert(anotherPerson.name);//"Greg"
alert(anotherPerson.school);//"kunming"
alert(yetAnotherPerson.friends);//"Shelby,Court,van,Rob,Barbie"
alert(yetAnotherPerson.name);//"Linda"

  Object.create()方法的第二个参数与Object。defineProperties()方法的第二个参数格式相同:每个属性都是通过自己的描述符定义的。

var person={
	name:"Nicholas",
	friends:["Shelby","Court","van"]
}
var anotherPerson=Object.create(person,{
	name:{
		value:"Greg"
	}
});
alert(anotherPerson.name);//"Greg"
  在没有必要兴师动众地创建构造函数,而只想让一个对象与另一个对象保持类似的情况下,原型式继承是完全可以胜任的。不过别忘了,包含引用类型的属性始终都会共享相应的值,就像使用原型模式一样。

(5)、寄生式继承

  寄生式继承是与原型式继承紧密相关的一种思路,并且同样由克罗克福德推而广之的。寄生式继承的思路与寄生构造函数和工厂模式类似,即创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像真地是它做了所有工作一样返回对象。

function creatAnother(original){
	var clone=object(original);//通过调用函数创建一个新对象
	clone.sayHi=function(){//以某种方式来增强这个对象
		alert("hi");
	}
	return clone;//返回这个对象
}
实例:

function object(o){
  function F(){}
  F.prototype=o;
  return new F(); 
}
function creatAnother(original){
	var clone=object(original);//通过调用函数创建一个新对象
	clone.sayHi=function(){//以某种方式来增强这个对象
		alert("hi");
	}
	return clone;//返回这个对象
}
var person={
	name:"Nicholas",
	friends:["Shelby","Court","van"]
}
var anotherPerson=creatAnother(person);//创建一个新对象anotherPerson
anotherPerson.sayHi();//"hi"

新对象anotherPerson不仅具有person的所有属性和方法,而且还有自己的sayHi()方法。在主要考虑对象而不是自定义类型和构造函数的情况下,寄生式继承也是一种有用的模式。


(6)、寄生组合式继承

组合继承是JavaScript最常用的继承模式;不过,它也有自己的不足。组合继承最大的问题就是无论什么情况下,都会调用两次超类型构造函数:一次是在创建子类型原型的时候,另一次实在子类型构造函数内部。

function SuperType(name){
this.name=name;
this.colors=["red","blue","green"];
}
SuperType.prototype.sayName=function(){
return 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(){
return this.age;
}
寄生组合式继承即通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。其基本思路是:不必为了指定子类型的原型而调用超类型的构造函数,我们所需要的无非就是超类型原型的一个副本而已。本质上,就是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型。

function object(o){
  function F(){}
  F.prototype=o;
  return new F(); 
}
function inheritPrototype(SubType,SuperType){
	var prototype=object(SuperType.prototype);//创建对象
	prototype.constructor=SubType;//增强对象
	SubType.prototype=prototype;//指定对象
}
function SuperType(name){
this.name=name;
this.colors=["red","blue","green"];
}
SuperType.prototype.sayName=function(){
return this.name;   
}
function SubType(name,age){
SuperType.call(this,name);//继承了SuperType
this.age=age;
}
inheritPrototype(SubType,SuperType);
SubType.prototype.sayAge=function(){
return this.age;
}
var instance1=new SubType("Nicholas",20);
instance1.colors.push("black");
alert(instance1.colors);//"red,blue,green,black"
alert(instance1.sayName());//"Nicholas"
alert(instance1.sayAge());//20
var instance2=new SubType("Greg",27);
alert(instance2.colors);//"red,blue,green"
alert(instance2.sayName());//"Greg"
alert(instance2.sayAge());//27










0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:665228次
    • 积分:8781
    • 等级:
    • 排名:第2239名
    • 原创:303篇
    • 转载:25篇
    • 译文:0篇
    • 评论:187条
    文章分类
    最新评论