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