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