javascript深入了解(继承)

目标:深入了解javascript继承;
实现方式:代码及相关文字解释,从最简单一步一步推算出最后的继承方式;

继承:继承其实就是通过原型链来实现的。
原型链:先回顾一下构造函数,原型和实例的关系:每个构造函数都有一个原型对象(prototype),原型对象包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针(__pro__)这些可以参考我上篇文章javascript深入了解(面向对象);由于是讲的两个模块所以在此就不重复了。
function Father(){
	this.familyName = 'Fan';
	this.name = 'aaa';
}
Father.prototype.showName = function(){
	alert(this.familyName+' '+this.name);
}
function San(){
	this.name = 'Lizhi'
}
San.prototype = new Father();
San.prototype.showName = function(){
	alert(this.name+' '+this.familyName);
}
var s = new San();
s.showName();//Fan Lizhi
San.prototype = new Father(); 就是将实例的Father赋值给 San的原型对象,实例后的Father包含构造函数Father的属性familyName,以及Father原型对象中的属性,也一起赋值给了San的原型,所以实例化San的时候同时也包含了Father中的属性。因此才实现了继承关系.当我们s.showName();时,我很会开始在对象里面一级级查找,首先我们会查找构造函数San(),我们先找San的构造函数没有这个方法,然后又开始查找san的原型链中,发现了从Father那里继承过来的showName();方法,于是返回。同样的访问顺序得到familyName和name。San.prototype = new Father();的意思其实就是 将实例化Father的属性赋值给San的原型。
San.prototype = {
	this.familyName : 'Fan',
	this.name : 'aaa',
	showName : function(){
		alert(this.familyName+' '+this.name);
	}
}
以上代码就是帮助我们理解。如果这个原理很清楚了那么就很容易理解下面的例子了:
function Father(){
	this.familyName = 'Fan';
	this.name = 'aaa';
}
Father.prototype.showName = function(){
	alert(this.familyName+' '+this.name);
}
function San(){
	this.name = 'Lizhi'
}
San.prototype = new Father();
San.prototype.showName = function(){
	alert(this.name+' '+this.familyName);
}
var s = new San();
s.showName();//Lizhi Fan 
这里在San.prototype = new Father();的下面重写了showName();所以就覆盖了原来的showName();如果将San.prototype = new Father();放在San.prototype.showName的下边,打印出来的将是Fan Lizhi,应为San.prototype = new Father();将原来的San.prototype 覆盖了。
讲到这里基本的继承已经出来了,但是也有问题,我们将Father里面的属性都做成了san的原型对象的属性(San.prototype),前面(面向对象一文)说过原型对象的属性是公用的,如果我们需要每一个San都有不同的familyName,也就是我们需要动态给Father构造函数传参数呢?,我们来修改一下上面的例子。也许这么做是个方法
function Father(familyName){
	this.familyName = familyName;
}
Father.prototype.showName = function(){
	alert(this.familyName+' '+this.name);
}
function San(familyName,name){
	Father.call(this,familyName);//第二次调用Father();
	this.name = name;
}
San.prototype = new Father();//第一次调用Father();
var s = new San('Fan','Lizhi');
alert(s.familyName+' '+s.name);// Fan lizhi
var s1 = new San('小','明');
alert(s1.familyName+' '+s1.name);// 小明
对了,用call或者apply都各异通过改变作用域来将原来Father的构造函数移到San中,成为San的构造函数。构造函数都是私有的。至于call和apply其实没必要知道原理,记着用法,以及作用就行了。作用就是将目标方法移到当前方法里执行。用法网上很多就不赘述了。
到这里继承已经说完了,但是这里还有个更好的实现方法,看上面的例子的两次调用Father,其实浪费了资源,于是有了下面的方法跟上面的区别就是效率更高了。第一次调用的原理上只需要继承 Father的原型对象(Father.prototype),不需要new 一个实例,实例是还包括构造函数对象的,而构造函数对象我们已经在Father.call(this,familyName);引用了的。所以有了下面的例子。
function Father(familyName){
	this.familyName = familyName;
}
Father.prototype.showName = function(){
	alert(this.familyName+' '+this.name);
}
function San(familyName,name){
	Father.call(this,familyName);
	this.name = name;
}
function inheritPrototype(Father,San){
	var oPrototype = Father.prototype;
	oPrototype.constructor = San;//constructor 更改指向的对象
	San.prototype = oPrototype;
}
inheritPrototype(Father,San);//将Father.prototype 赋值给San.prototype
var s = new San('Fan','Lizhi');
alert(s.familyName+' '+s.name);// Fan lizhi
var s1 = new San('小','明');
alert(s1.familyName+' '+s1.name);// 小明
其实inheritPrototype 就是将原来的Father整个实例赋值给San的原型(San.prototype)改为Father.prototype 赋值给San的原型(San.prototype),YUI的YAHOO.lang.extend()方法就是采用的这种继承模式。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值