已经忘了多久没拿起javascirpt的书本了,无聊中拾起快被我遗忘在角落的书本<javascript高级程序设计>。
尼古拉斯竟然把这么奇葩的js面向对象说的这么好
从最原始的工厂模板说起
function createPerson(name,age){
var o = new Object();
o.name = name;
o.age = age;
o.sayName = function(){
console.log(this.name);
}
return o;
}
这样,but...等等,如何知道创建的对象是什么类型的?
构造函数模式
function Person(name,age){
this.name = name;
this.age = age;
this.sayName = function(){
}
var person1 = new Person("三毛",25);
简洁了不少,问题又来了:难道我每次new一个对象,就要重新创建个sayName函数吗?为什么这么说?
函数也是对象,每次实例化Person时,都会实例化一个sayName Function。也就是说:每个Person的示例,都有一个不同的sayName Function实例。何必如此?
理解原型模式
原型模式很好的解决了上述问题,这么说吧,每个function都有一个prototype属性(我们叫原型),指向一个原型对象。
所以,我们可以把公共的方法属性都加到它上面。
function Person(){
}
Person.prototype.name = "三毛";
Person.prototype.age = 25;
Person.prototype.sayName = function(){
console.log(this.name);
}
把属性方法加到了函数的prototype(实际上也是个对象)上,后续创建的实例对象都从这个原型对象上继承了属性和方法。
然而,它也不是完美的。原型对象的问题也是显而易见的:由于所有的实例都是共享原型对象的方法属性,那么意思就是说所有的实例共享了一份属性方法!那创建的实例还有个毛用?
联想到前面我们提过的构造函数模式,我们可以把构造函数+原型模式结合起来。
构造函数负责自有的属性,而原型对象负责公共的方法,一种新的模式应运而生:
组合模式
Person.prototype.sayName = function(){
console.log(this.name);
}
完美解决了构造函数模式和原型模式的问题。
再谈谈javascript继承
在“理解原型模式”中,已经谈到了js的原型模式,而js的继承就是依靠原型链来实现的。
js原型链
我们来简单回顾下前面讲过的内容:
1 每个构造函数都有一个原型对象;
2 原型对象包含一个指向构造函数的指针(证明自己是从哪来的);
3 构造函数的实例(这么说可能有点不准确)包含一个指向原型对象的指针。
有点晕,感觉有点像三角恋,看个实例:
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function(){
return this.property;
}
function SubType(){
this.subproperty = false;
}
//继承,使SubType的原型指向SuperType的原型
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function(){
return this.subproperty;
}
var instance = new SubType();
console.log(instance.getSuperValue());//true,实际上是SuperType的值
看以看出,单独使用原型链实现继承,实际上生成的实例都是共享一份属性或者方法的。
那么,实例就没有意义了。
那如果借用构造函数呢?
我们知道,构造函数只能实现对实例属性的继承,没办法实现方法的继承,所以如果单独使用构造函数模式,方法就需要都写在构造函数里面,函数复用就无从谈起了。
组合继承
组合继承借用了原型链实现原型属性和方法的继承,又可以通过构造函数实现实例属性的继承。可以说既实现了函数复用,又保证每个实例都有自己的属性。
function SuperType(name){
this.name = name;
this.colors = ["red", "blue"];
}
SuperType.prototype.sayName = function(){
console.log(this.name);
}
function SubType(name,age){
SuperType.call(this,name);//在当前实例环境下执行SuperType构造函数->2
this.age = age;
}
SubType.prototype = new SuperType();//->1
SubType.prototype.sayAge = function(){
console.log(this.age);
}
看以看到,这种模式结合了两者的优点,又弥补了相互的不足。可以说,是一种不错的用于实现继承的方法。
但是,这种方法并不是最好的,我们来详细看一下:
1 为了实现继承,第一点调用了SuperType的构造函数,这样SubType.prototype继承了两个属性
2 SubType构造函数内部,又调用了一次SubType构造函数,显得有些多余了
寄生组合继承
通过借用构造函数来继承属性,通过原型链来继承方法。
function object(super){
var func = function(){};
func.prototype = super;
var o = new func();
return o;
}
function inheritPrototype(sub,super){
var prototype = object(super.prototype);
prototype.constructor = sub;
sub.prototype = prototype;
}
这是寄生组合继承的简单形式。
通过接受子构造函数和父构造函数,第一步创建父类型原型的一个副本;
第二步为副本添加constructor属性,最后再把新的原型赋给子原型,完美实现了继承关系。
网友评论