js中的继承

  js的继承和诸如C#、java等面向对象的继承在原理上有很大的差别:js的继承借助this关键字和原型对象prototype实现。

对象冒充:

  借助this关键字,给所有的属性、方法赋值,也就是采用类声明的构造函数方式。因为构造函数只是一个函数,所以,可以使ClassA的构造函数,成为ClassB的一个方法,然后调用它,这样一来,ClassB就收到了ClassA的构造函数中定义的属性和方法。

对象冒充实现继承:
function ClassA(sColor){
	this.color=sColor;
	this.sayColor=function(){
		alert(this.color);
	}

}

function ClassB(sColor,sName){

	this.newMethod=ClassA;//为ClassA赋予方法newMethod(函数名只指向它的指针),这里的ClassA是作为常规的函数来建立基础机制,而不是作为构造函数,也可以理解成,将构造函数当做常规函数来处理。
	this.newMethod(sColor);//调用newMethod方法,传递的是ClassB的构造参数sColor
	delete this.newMethod;//删除对ClassA的引用后,就不再调用它,所有的新属性和方法都必须在删除了新方法后定义,否则可能会覆盖超类的相关属性和方法。
	
	this.name=sName;
	this.sayName=function(){
		alert(this.name);
	}
}

//调用
var objA= new ClassA("red");
var objB = new ClassB("blue","Nicholas");
objA.sayColor();//red
objB.sayColor();//blue
objB.sayName();//Nicholas


  对象冒充是可以实现多继承的:

  例如,ClassZ要继承ClassX和ClassY: 

ClassZ继承ClassX和ClassY
function ClassZ(){
	this.newMethod=ClassX;
	this.newMethod();
	delete this.newMethod;
	
	this.newMethod=ClassY;
	this.newMethod();
	delete this.newMethod;
}

  这里有个弊端,如果ClassX和ClassY具有相同的属性和方法,ClassY具有高优先级,因为它是从后面的类继承。

  其实上面的这些方法只为更好的理解一下js中的继承的实现思路,而js中经常使用的是call和apply方法
  他们的第一个参数都用作this的对象,其他参数传递给函数自身:call方法中是逐个的参数,apply是参数数组。

function ClassB(sColor,sName){
	//this.newMethod=ClassA;
	//this.newMethod(sColor);
	//delete this.newMethod;
	
	ClassA.call(this,sColor);		//与上面的直接使用对象冒充是等效的
	//ClassA.apply(this,new Array(sColor));	//如果是apply,第二个参数则是数组
	//ClassA.apply(this,arguments);		//使用js的内部对象arguments
	


	this.name=sName;
	this.sayName=function(){
		alert(this.name);
	};
}


  call的一个单独示例:

function sayColor(sPrefix,sSuffix){
	alert(sPrefix+this.color+sSuffix);
};

var obj= new Object();
obj.color="red";

sayColor.call(obj,"The color is","a very nice color indeed");


  apply的一个单独示例:

function sayColor(sPrefix,sSuffix){
	alert(sprefix+this.color+sSuffix);
};
var obj = new Object();
obj.color="red";
//将一个方法绑定到一个对象上
sayColor.apply(obj,new Array("The color is","a very nice color indeed"));


  在js中会大量用到call和apply,使用的过程中,也会有些注意的地方,这里先不做赘述。这里我们只需理解:call和apply函数都可以将一个函数绑定到一个对象上,或者是在子对象中继承父类的方法。两者的差异是给调用的函数,传递参数的方式不同(call是参数,apply是参数数组,且往往和arguments配合使用,需要父类和子类的参数顺序完全相同,否则需要重新定义一个数组)。

说到js的继承,必须要提的是prototype原型链:

  prototype对象相当于一个模板,要实例化的对象都会以这个模板为基础,那么,在prototype模板中定义的属性和方法,自然会被继承到它的实例中,这就是原型链的原理。

  针对上面的例子,使用原型链实现继承:  

function ClassA(){ }
ClassA.prototype.color="red";
ClassA.prototype.sayColor=function(){
	alert(this.color);
};

function ClassB(){
	
}
//原型链中标准的做法是在调用ClassA的构造函数的时候没有任何参数
ClassB.prototype= new ClassA();
ClassB.prototype.name="";
ClassB.prototype.sayName=function(){
	alert(this.name);
};

调用:
var objA= new ClassA();
var objB= new Classb();
objB.color="red";
objA.color="blue";
objB.name="Nicholas";
objA.sayColor();//red
objB.sayColor();//blue
objB.sayName();//Nicholas

有意思的是再原型链中,instanceof 运算符的运算方式也很独特,对ClassB的所有实例,instanceof 为ClassA和ClassB都返回true

var objB= new ClassB();
alert(objB instanceof ClassA);//true
alert(objB instanceof ClassB);//true


 使用prototype需要注意的几个地方:

1、原型链中标准的做法是在调用ClassA的构造函数的时候没有任何参数。

2、instanceof 运算符的运算方式也很独特,对ClassB的所有实例,instanceof 为ClassA和ClassB都返回true。

3、使用原型链的方式,可以批量的将ClassA的属性和方法都赋予给ClassB,而prototype对象的作用就是封装这些参数。

4、子类中所有的属性和方法都需要在prototype属性被赋值后,因为,如果在它赋值前定义,那么后面再次赋值之后都会被替换掉。

5、使用原型链的一个弊端是,不支持多重继承,另外会用父类的的对象重写子类的原型链的prototype属性。

综上:对象冒充的弊端是必须使用构造函数方式,具有局限性;使用原型就无法使用带参数的构造函数了,那么解决这个问题呢?

 答案是:采用构造函数的方式定义属性,采用原型的方式定义方法。

采用构造函数定义属性,原型的方法定义方法:
function ClassA(sColor){
	this.color=sColor;
}

ClassA.prototype.sayColor=function(){
	alert(this.color);
}

function ClassB(sColor,sName){
	//ClassA的call方法,对象冒充继承ClassA类的sColor属性。
	ClassA.call(this,sColor);
	this.name=sName;
}

ClassB.prototype= new ClassA();//使用原型链的方式,继承ClassA的方法(构造函数没有参数),因为使用了prototype,所以,instanceof运算符仍能正确的运行。


ClassB.prototype.sayName=function(){
	alert(this.name);
};


调用:
var objA = new ClassA("red");
var objB = new ClassB("blue","Nicholas");

objA.sayColor();//red
bojB.sayColor();//blue
bojB.sayName();//Nicholas

  这里的继承由ClassA.call(this,sColor)和ClassB.prototype= new ClassA()实现。
  使用原型链的方式,继承ClassA的方法(构造函数没有参数),因为使用了prototype,所以,instanceof运算符仍能正确的运行。
  ClassA的call方法,对象冒充继承ClassA类的sColor属性。

   经过一些列的演变,找到了js中最优秀的继承继承机制:采用构造函数的方式定义属性,采用原型的方式定义方法。这样在子类的定义中,既可以使用带参数的构造函数,也可以使用prototype批量的继承父类的方法。

  另外,这里使用到了js中两个常用的方法:call和apply。他们不仅可以在类的继承中发挥很大的作用,还在将一个方法捆绑到另一个对象的时候经常使用。

  使用call和apply的区别在于:参数的传递方法(call传递的是逐个的参数,apply传递的是一个数组,很多时候配合arguments使用,但是要求继承的父类和子类的函数的参数顺序是一样的)。

  这两个方法在使用的时候,还需要注意的地方,另外js中还有其他扩展或者捆绑的函数observer() bindAsEventListener(),也可以重新定义bindAsEventListener的绑定方法,将在后续的文章中深入学习。

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值