javascript继承

之前看《javascript高级程序设计》的几种继承实现的方式,每过一段时间就会忘记这几种方式,所以写下此文。

继承实现有以下几种方式:

1、原型链

使用原型链作为实现继承,其思想是通过对象可以访问其原型链上的属性和方法。

实现方法:

让原型对象等于另一个类型的实例。

代码如下:

function SuperType(){
	this.property = true;
}
SuperType.prototype.getSuperValue = function(){
	return this.property;
};
function SubType(){
	this.subproperty = false;
}
//继承了SuperType
SubType.prototype = new SuperType();
SuperType.prototype.getSubValue = function(){
	return this.subproperty;
};
var instance = new SubType();
alert(instance.getSuperValue()); //true

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


上述代码,并没使用SubType默认提供的原型,是使用了SuperType的实例作为原型。因此,这个原型不仅拥有SuperType的实例上的全部属性和方法,而且其内部

还有一个指针,指向SuperType的原型。具体过程是这样的:instance指向SubType的原型,SubType的原型又指向SuperType的原型。getSuperValue()

方法仍然还在SuperType.prototype中,而property则位于SubType.prototype中,这是因为property是一个实例属性,而getSuperValue()则是一个原型方法。

既然SubType.prototype现在是SuperType实例,那么property当然就位于该实例中。

注意:a)instance.constructor现在指向的是SuperType,这是因为SubType.prototype中的constructor被重写了。

    b)  通过原型链实现继承时,不能使用对象字面量创建原型方法。因为这样做就会重写原型链。


这种方式的缺点:

a) 包含引用类型值的原型属性会被所有实例共享

b) 在创建子类型的实例时,无法向父级的构造函数中传递参数


2、借用构造函数

这种技术的思想是在子类构造函数的内部调用父级的构造函数。通过使用apply或call方法可以在新创建的对象执行构造函数。

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"

注意:为了避免父级构造函数重写子类型的属性,应该在调用父级构造函数后,再添加子类型中定义的属性。

这种方式优点是:可以在子类构造函数中向父类构造函数传递参数,解决了原型链继承的问题。

缺点是:无法避免构造函数模式存在的问题(方法都必须在构造函数中定义),因此失去了复用性。


3、组合继承

 也叫伪经典继承,它将原型链和借用构造函数的技术组合到一块。其背后的思想是使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。

<script type="text/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);
	this.age = age;
}

//继承方法
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){
	alert(this.age);
};

var instance1 = new SubType("Nsicholas", 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
组合继承避免了原型链和借用构造函数的缺陷,融合它们的优点,称为JavaScript中最常用的继承模式。而且,instanceof和isPrototypeOf()也能够用于识别基于组合继承创建的对象。

组合继承最大的问题就是无论什么情况下,都会调用两次父类构造函数:一次在创建子类原型的时候,另一次是在子类构造函数内部。子类最终会包含超类型对象的全部实例属性,但我们不得不在调用子类型构造函数时,重写这些属性。

代码演示:

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);
};
上面代码,在第一次调用SuperType构造函数时,SubType.prototype会得到两个属性:name和colors.他们都是SuperType的实例属性,只不过是位于SubType

的原型上。当调用SubType构造函数时,又会调用一次SuperType构造函数,这一次又在新对象上创建了实例属性name 和colors。而这两个属性就屏蔽了原型中的两个同名属性。


4、原型式继承

这种继承的思路是:借助原型可以基于已有的对象创建新对象,同时还不必因此创建自定义类型。

通过下面这个函数可以达到目的。

function object(o){
	function F(){}
	F.prototype = o;
	return new F();
}

这个函数的工作是,先创建了一个临时的构造函数,然后把它的原型设置为所传人的对象,最后返回这个临时构造函数的新实例。从本质上说,该函数对传人的对象

进行了一次浅拷贝。

请看下面使用的例子:

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.friends.push('Rob');

var yetAnotherPerson = object(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");

alert(person.friends); //"Shelby,Court,Van,Rob,Barbie"
注意:以这种方式指定的任何属性都会覆盖原型对象上的同名属性。

这种方式的优点:就是不用那么兴师动众地创建构造函数,在只想让一个对象与另一个对象保存类似的情况下可以使用。

缺点:它和原型链模式一样,当包含引用类型值的属性时,都会共享该属性值。


5、寄生式继承

寄生式(parasitic)继承是与原型式继承紧密相关的一种思路。寄生式继承的思路与寄生构造函数和工厂模式类似,即创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后像真的是它做了所有工作一样返回对象。

代码演示:

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

这个例子基于person返回一个新对象anotherPerson.新对象不仅具有person的所有属性和方法,而且还有自己的sayHi()方法。

当对象对象不是自定义类型和构造函数的的情况下,寄生式继承也是一种有用的模式。任何能返回新对象的函数都适用于此模式。

缺点:

与借用构造函数一样,做不到函数的复用。


6、寄生组合式继承

组合继承出现两次调用父类构造函数,为了解决这个问题,产生了寄生组合式继承。它是通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。其思想是:

不必为了指定子类的原型而调用父类的构造函数,我们所需要的是原型的一个副本而已。本质上,就是使用寄生式继承来继承父类的原型,然后再将结果指定给子类原型。

寄生组合式实现:

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;				 //指定对象
}

这段代码实现,在函数内部,第一步是创建父类原型的一个副本。第二步是为创建的副本添加constructor属性,从而修正了constructor属性。最后一步,将新创建的对象赋值给子类的原型。

寄生组合式应用示例:

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(){
	alert(this.name);
};
function SubType(name, age){
	SuperType.call(this, name);

	this.age = age;
}
inheritPrototype(SubType, SuperType);
SubType.prototype.sayAge = function(){
	alert(this.age);
};

寄生组合式继承只调用了一次SuperType构造函数,并且避免了在SubType.prototype上创建不必要的、多余的属性。开发人员普遍认为寄生组合式继承是引用类型最理想的继承范式。YUI的YAHOO.lang.extend()采用了这个模式。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值