js里面是如何做到继承的呢?依靠一个东西叫原型链,所谓原型链,就是上一节所提到的原型组成的链条。(这到底是什么鬼)
之前说到每一个构造函数都有一个prototype属性,由构造函数new出来的实例都有一个指向prototype的内部指针。
那么,如果我们让一个构造函数的prototype等于另外一个对象(构造函数为A)的实例,那么会发生什么呢?
会发生的就是这个prototype对象中会有一个内部指针,指向A.prototype;
然后我们又可以将A.prototype等于其他的对象实例,就这么一而再,再而三,一条原型的链条就这么诞生了,这就是所谓的原型链。
function SuperType(){
this.name = "Super";
}
SuperType.prototype.getSuperName(){
return this.name;
}
function SubType(){
this.subName="sub";
}
SubType.prototype = new SuperType();
SubType.prototype.getSubName = function(){
return this.subName;
}
var obj = new SubType();
alert(obj.getSuperName());//Super
又包含了一个内部指针指向了SuperType.prototype,因此形成了原型链。因为原型链的存在,最终使得SubType的实例obj中可以调用
SuperType.prototype中定义的getSuperName方法(回顾一下上一节讲到的属性对象搜索过程,只是比上一节所讲的扩展了一下)。
原型链的问题:
仍旧是原型所固有的问题,就是共享,上一节中使用了属性初始化和方法初始化分离的方法解决了共享,但是这里又不同了,因为我们的
SubType的原型指向的是SuperType的实例,因此,很简单的,SuperType中的任何属性对象都会被SubType的对象所共享。然后,总会
有些不爽的事情发生,那肿么办呢?
借用构造函数:
啥叫借用构造函数,实际上就是运用了function.apply和function.call方法,强行修改了父类构造函数执行环境,例子如下:
function SuperType(name){...}
function SubType(name,type){
this.type = type;
SuperType.call(this,name);
}
SubType.prototype = new SuperType();
这里我们看到,虽然仍然是使用了SuperType的实例作为SubType的实例,但是我们在SubType的构造函数中借用了SuperType的构造函数来为
SubType的实例进行了初始化,实质上是让每一个SubType的实例在初始化之后都覆盖掉了自己原型中的属性,从而打破了属性的共享。这种继承
也被称之为组合式继承。这种继承方式很大程度上解决了js对象继承中所遇到的问题,但是,它仍然会有自己的问题,这个问题就是,SubType的
prototype中仍然存在着SuperType的属性,虽然被覆盖掉无法访问,但它毕竟是存在着的。随着原型链长度的增加,原型链中被不应该存在的属性
对象所占用的内存空间将会越来越大,因此,我们仍然需要对它进行改进。
原型式继承:
先把代码贴出来:
function object(o){
function F(){};
F.prototype = o;
return new F();
}
这种原型式的继承想法是要借助原型在已有的对象的基础上创建新的对象,我的理解是,这种继承方式,把继承从具体的对象类型上剥离出来,转
而将注意力着重在了原型对象的传递上。可能我这么说比较抽象,我们就来看看后面的这种继承方式:
寄生组合式继承:
function SuperType(name){
this.name = name;
}
SuperType.prototype.getSuperName(){
return this.name;
}
function SubType(name,type){
this.type = type;
SuperType.call(this,name);
}
SubType.prototype = object(SuperType.prototype)
SubType.prototype.getSubName = function(){
return this.subName;
}
这里的object方法是借用了之前所提到的object方法,这里我们来思考一下,这种继承的好处是什么,其实区别就在于SubType赋值的时候,
没有new SuperType。我认为,这里就体现了我之前所提到的,将继承集中在了原型上,object方法的调用实际上就是把SuperType中我们
不需要的name属性从object返回的对象中剥夺了,相当于我们只是继承了SuperType.prototype中我们想要的方法,至于属性,留给借用构造
函数去完成,从而解决了上一种继承中冗余属性对象占用空间的问题。(即书中所讲的,组合式继承调用了两次SuperType的构造方法,而寄
生式组合继承只调用了一次)。
可能我讲的有点快,可能我讲的有点乱,希望有兴趣的朋友大家一起讨论!
下一节将说到闭包,这也是个重要概念啊!