刚接触Js的童鞋会很不习惯Js的prototype继承,不管是C++,Java,还是Python 都有完整的类继承机制,如果把以前的思路带到Js中,你会吃不少的亏,所以我们首先要做的就是转换思路,将Js的继承机制当作新的东西学习,也不要疑惑为啥Js的继承这么麻烦,为什么没有Class的支持呢?统统的一切疑惑你都要换个思路,如果你最早接触的开发语言是Js,当你再遇到C++时,你也会有一样的疑惑,所以告诉自己人家就是这么实现的,Just do it。
一:prototype和constructor
首先看一下下面的代码(全部Js代码均可在firebug控制台中运行)
function People(name,age){
this.name=name;
this.age=age;
this.getName=function(){
return this.name;
}
}
我想大家想象的类应该这么写,可这么写我们会发现getName这个函数和name变量一样,也就是新建的每一个对象都要定义这个函数,貌似有点浪费资源,有下为证:
var a = new People('A',18);
var b = new People('B',19);
console.log(a.getName==b.getName);
输出结果为:false
你说Js也真是的,不提供class支持也就罢了,成员函数还不可以写在构造函数中,啥你说构造函数,是的,function People就是People类的构造函数。其实和一般函数没有区别,我们也可以直接运行。
People('C',20);
console.log(window.name);
console.log(window.getName());
程序输出结果:均为C
由于我们是直接调用的,所以this没有指定,那么默认就是全局的window对象。
而且对象的构造函数我们是可以得到引用的,
console.log(a.constructor==b.constructor);
console.log(a.constructor==b.constructor);console.log(a.constructor==b.constructor); 输出的结果是:true,
那我们在Js中应该怎么写类呢?这就涉及到Js独特的prototype了。
function People(name,age){
this.name=name;
this.age=age;
}
People.prototype.getName=function(){
return this.name;
}
People.prototype.getAge=function(){
return this.age;
}
prototype是每一个类都有的一个属性,这个属性说白了就是一个对象,那么对象肯定是有属性的,你new的每一个对象都将拥有该对象的所有属性。
那么如果自己定义类的prototype对象,也就实现了我们给类定义成员函数的目的。
所以上面代码等价于:
function People(name,age){
this.name=name;
this.age=age;
}
People.prototype={
getName:function(){
return this.name;
},
getAge:function(){
return this.age;
}
};
现在我们都懂了,原来只要在类的prototype属性中设置任何我们想要的,实例化后的每个对象都将拥有该属性,多么完美啊。
我们看看实例后的对象是否拥有这些方法:
var a = new People('A',18);
for(x in a )
console.log(x.toString());
输出:
果然,实例后的对象都拥有prototype对象拥有的属性。
既然People.prototype也是对象,那么我们应该也可以将它的属性打出来。
for(x in People.prototype)
console.log(x.toString());
输出:
那又怎能确定是引用prototype的属性而不是复制呢?
function People(name,age){
this.name=name;
this.age=age;
}
People.prototype={
id:'I am a People'
};
var a = new People('A',18);
var b = new People('B',19);
console.log(a.name==b.name);
console.log(a.id==b.id);
输出结果: false true
从上面我们可以看出,的确使用的是引用。
那我要修改a.id怎么办,b.id会变吗?想想如果你是设计人员,你会怎么做?
var aid=a.id
a.id="i am A";
console.log(b.id);
console.log(aid===b.id);
程序输出:
I am a PeopleI am a People true
正如你所想的,肯定将重新绑定a.id了。
到这里其实大家对单实例的prototype肯定都没有问题了,这里还需要扩展一下constructor属性。
其实,通过(1)People.prototype.getName=function(){}:和(2)People.prototype={};还是有一点区别,区别就在于prototype是否丢弃了constructor属性的引用。
对象的实例和prototype都拥有constructor属性(构造函数的引用)
通过(1),People.protorype是由constructor属性的,测试如下:
console.log(People.prototyp.constructor===People);
将会输出true
而通过(2)将会输出:false。
二:继承
看到这里肯定对prototype熟悉了,那么继承就非常简单了。
function People(name,age){
this.name=name;
this.age=age;
}
People.prototype={
getName:function(){
return this.name;
},
getAge:function(){
return this.age;
}
};
function Boy(name,age,shape){
People.call(this,name,age);
this.shape=shape;
}
Boy.prototype=People.prototype;
Boy.prototype.getShape=function(){
return this.shape;
};
var boy=new Boy('kitty',6,"fat");
console.log(boy.getName());
console.log(boy.getShape());
Boy类继承People类,所以最简单的就是让Boy的prototype等于People的prototype,这样Boy将拥有People.prototype的所有属性,最后再添加自己需要的成员函数即可。
其实我们也可以在Boy的构造函数中将People.prototype的所有属性添加到Boy.prototype,构造函数代码修改如下:
function Boy(name,age,shape){
People.call(this,name,age);
this.shape=shape;
for(f in People.prototype){
Boy.prototype[f]=People.prototype[f];
}
}