首页 > 编程语言 > 详细

JavaScript面向对象初探——封装和继承

时间:2016-01-29 11:50:36      阅读:130      评论:0      收藏:0      [点我收藏+]

JavaScript中的对象就是类,没有class之类的关键字,但不妨碍它是一门面向对象语言。它的面向对象是基于prototype(原型)的,而不像Java这种基于类。Object是所有类(对象)的基类,它没提供什么可用的。

 

1.prototype

所有对象都拥有prototype属性,可以借助它给对象添加新属性和方法(function是方法,除此之外都是属性)。下面的代码给Object添加了一个属性和一个方法,对象调用和实例调用都好使。运行这段代码会弹出2次"javascript"。

1 <script type="text/javascript">
2     Object.prototype.myName = "javascript";
3     Object.prototype.hi = function(){
4             alert(this.myName);
5     } 
6     Object.hi();
7     var obj = {};
8     obj.hi();
9 </script>        

给实例加属性的话,只有这个实例才可以用。这里就不需要写prototype了。

1 <script type="text/javascript">
2     var obj = {};
3     obj.myName = "javascript";
4     obj.hi = function(){
5         alert(this.myName);
6     } 
7     //Object.hi();  //提示Object.hi不是函数
8     obj.hi();
9 </script>

但如果先声明一个实例,对象后加新属性或方法,这个实例也会拥有相应的新属性或方法。W3C提醒我们要知道这点,但不推荐利用它。

 

理解到这,我们可能按奈不住想给JS对象改改刀了。是的,利用prototype可以扩展原生对象,也可以修改。下面代码给String对象添加了一个trim方法。

1 String.prototype.trim = function(){
2     return this.replace(/\s+/g,""); //去掉所有空格
3 }
4 var str="as df g";
5 alert($(str.trim());

原生对象的自有方法怎么修改?答案是添加一个同名新方法。被除去引用的original方法就被JS的垃圾回收机制自动销毁,如果还想使用original方法这样就太不完美了。可以像下面那样把original方法的引用保存起来。

Object.prototype.originalToString = Object.toString();
Object.prototype.toString = function(){  
   //todo 
};

 

2.封装类

JS对象成员的作用域都是公有的,开发人员用下划线开头的命名方式表示私有,虽然仍然能被访问。

怎么封装一个类,也就是创建自定义对象?参考了W3C的代码。

  • 构造函数式
 1 function Car(sColor,iDoors,iMpg) {
 2   this.color = sColor;
 3   this.doors = iDoors;
 4   this.mpg = iMpg;
 5   this.showColor = function() {
 6     alert(this.color);
 7   };
 8 }
 9 
10 var oCar1 = new Car("red",4,23);
11 var oCar2 = new Car("blue",3,25);

和Java模式最像。缺点是每生成实例都会创建一次showColor。

  • 原型方式
 1 function Car() {}
 2 
 3 Car.prototype.color = "blue";
 4 Car.prototype.doors = 4;
 5 Car.prototype.mpg = 25;
 6 Car.prototype.showColor = function() {
 7   alert(this.color);
 8 };
 9 
10 var oCar1 = new Car();

利用了前述prototype。缺点是不灵活,无法传参数。

 

W3C推荐两种流行方式封装类。

  • 构造函数和原型混合式
 1 function Car(sColor,iDoors,iMpg) {
 2   this.color = sColor;
 3   this.doors = iDoors;
 4   this.mpg = iMpg;
 5   this.drivers = new Array("Mike","John");
 6 }
 7 
 8 Car.prototype.showColor = function() {
 9   alert(this.color);
10 };
11 
12 var oCar1 = new Car("red",4,23);
13 var oCar2 = new Car("blue",3,25);
  • 动态原型式
 1 function Car(sColor,iDoors,iMpg) {
 2   this.color = sColor;
 3   this.doors = iDoors;
 4   this.mpg = iMpg;
 5   this.drivers = new Array("Mike","John");
 6   
 7   if (typeof Car._initialized == "undefined") {
 8     Car.prototype.showColor = function() {
 9       alert(this.color);
10     };
11     
12     Car._initialized = true;
13   }
14 }

这两种方式都是为了避免重复创建函数副本。

 

3.继承

我使用了混合式声明了我的类,下面要再声明一个子类继承父类的所有属性和方法。这里使用了call和prototype。它们都能以不同的方式实现继承,我一起使用它们更方便比较。

 1 function BMW(sColor,iDoors,iMpg,len){
 2     Car.call(this,sColor,iDoors,iMpg); //继承父类Car的属性,可以是一部分. 详见aguments对象可实现重载
 3     this.len = len;  //子类自有属性
 4 };
 5 BMW.prototype = new Car();  //获得父类Car的所有方法
 6 BMW.prototype.showLen = function(){  //子类自有方法
 7     alert(this.len);
 8 };
 9 var x5 = new BMW("green",4,35,5);
10 x5.showColor(); 
11 x5.showLen();   //结果弹出"green","5"
  • 上面代码第2行使用了call继承父类Car的属性。

原理:call方法的用法是func.call(obj,args),使obj可以调用本不是自己的方法func,从而改变this,args是参数。与它类似的是apply方法,只是传参时参数是数组形式。

  • 代码第5行使用了prototype继承父类所有方法。

原理:对象的任何属性和方法都被传递给那个对象的所有实例。子类的prototype设置成父类的实例,就继承了父类的所有属性和方法。但子类的 prototype 属性被覆盖了,原来的方法不复存在,所以子类自有属性需要在prototype设置之后添加。

继承的顺序:

上面的代码是先使用call继承属性,同时添加自有属性,再使用prototype继承方法,最后添加自己方法。不要疑惑这个顺序会产生覆盖。如果第5行改为BMW.prototype = new Car(‘yellow‘,3,25)才会覆盖。原因是JS的aguments对象,它的特性就不在这里细说了,可以利用它实现函数重载。其实使用call并没有继承到父类的任何属性,因为父类构造函数的参数undefined,父类实例中并没有添加上前几个几个属性,drivers属性有。

call和prototype的区别:

显而易见,call方法可以方便的实现多继承,而原型链方法不能支持多继承。另外,call方法继承可以获取父类的一部分属性,而prototype继承只是全盘接收。

 

JavaScript面向对象初探——封装和继承

原文:http://www.cnblogs.com/feitan/p/5166816.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!