JavaScript的模块化:继承(原型)、封装(闭包)、多态

JavaScript的模块化:继承(原型)、封装(闭包)、多态

 

一、封装:

  封装是实现面向对象程序设计的第一步,封装就是将数据或函数等集合在一个个的单元中(我们称之为类)。被封装的对象通常被称为抽象数据类型。 

 封装的意义:

  封装的意义在于保护或者防止代码(数据)被我们无意中破坏。在面向对象程序设计中数据被看作是一个中心的元素并且和使用它的函数结合的很密切,从而保护它不被其它的函数意外的修改。

  封装提供了一个有效的途径来保护数据不被意外的破坏。相比我们将数据(用域来实现)在程序中定义为公用的(public)我们将它们(fields)定义为私有的(privat)在很多方面会更好。私有的数据可以用两种方式来间接的控制。第一种方法,我们使用传统的存、取方法。第二种方法我们用属性(property)。

  使用属性不仅可以控制存取数据的合法性,同时也提供了“读写”、“只读”、“只写”灵活的操作方法。

访问修饰符:

Private:只有类本身能存取.

Protected:类和派生类可以存取.

Internal:只有同一个项目中的类可以存取.

Protected Internal:是Protected和Internal的结合. 

Public:完全存取.

 二、继承:

  继承主要实现重用代码,节省开发时间。

1、C#中的继承符合下列规则:

1.继承是可传递的。如果C从B中派生,B又从A中派生,那么C不仅继承了B中声明的成员,同样也继承了A中的成员。Object类作为所有类的基类。

2.派生类应当是对基类的扩展。派生类可以添加新的成员,但不能除去已经继承的成员的定义。

3.构造函数和析构函数不能被继承。除此之外的其它成员,不论对它们定义了怎样的访问方式,都能被继承。基类中成员的访问方式只能决定派生类能否访问它们。

4.派生类如果定义了与继承而来的成员同名的新成员,就可以覆盖已继承的成员。但这并不因为这派生类删除了这些成员,只是不能再访问这些成员。

5.类可以定义虚文法、虚属性以及虚索引指示器,它的派生类能够重载这些成员,从而实现类可以展示出多态性。

  2、new关键字

   如果父类中声明了一个没有friend修饰的protected或public方法,子类中也声明了同名的方法。则用new可以隐藏父类中的方法。(不建议使用)

  3、base关键字

   base 关键字用于从派生类中访问基类的成员:

1.调用基类上已被其他方法重写的方法。

2.指定创建派生类实例时应调用的基类构造函数。

三、多态

  1、多态:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。在运行时,可以通过指向基类的指针,来调用实现派生类中的方法。

  编译时的多态性:

  编译时的多态性是通过重载来实现的。对于非虚的成员来说,系统在编译时,根据传递的参数、返回的类型等信息决定实现何种操作。 

  运行时的多态性:

  运行时的多态性就是指直到系统运行时,才根据实际情况决定实现何种操作。C#中,运行时的多态性通过虚成员实现。 

  编译时的多态性为我们提供了运行速度快的特点,而运行时的多态性则带来了高度灵活和抽象的特点。

   2、实现多态:

1.接口多态性。

2.继承多态性。

3.通过抽象类实现的多态性。

  3、override关键字:

   重写父类中的virtual修饰的方法,实现多态。

 

javascript面向对象。下面详细描述如何实现?

    第一步,如何定义类?

    在javascript中class是保留字,没有class如何定义类?利用javascript中的function关键字,类名首字母一般采用大写,下面,我来定义User类。

1 function User(userid,uname,uage,uscore){ 

2      this._userid=userid; 

3      this._name=uname; 

4      this._age=uage; 

5      this._score=uscore; 

6  } 

   User类已经定义好了,函数User相当于类User的构造函数,this指针(严格意义上this不是指针)指向的变量即是User类的数据成员(或者叫属性),访问权限是public,下面我们来添加方法。

01 function User(userid,uname,uage,uscore){ 

02         this._userid=userid; 

03         this._name=uname; 

04         this._age=uage; 

05         this._score=uscore; 

06   

07          //也许你会这样添加函数方法 

08   

09          this.downloadResource=function(){ 

10                return "User类:下载资源"; 

11          }; 

12          this.shareResource=function(){ 

13                return "User类:分享资源"; 

14          }; 

15          this.showInfo=function(){ 

16          var str_tmp=""; 

17          str_tmp+="userid:"+this._userid+"<br/>"; 

18          str_tmp+="name:"+this._name+"<br/>"; 

19          str_tmp+="age:"+this._age+"<br/>"; 

20          str_tmp+="score:"+this._score+"<br/>"; 

21          return str_tmp; 

22          }; 

23  } 

     看见注释了吗?最直接的办法就是给类添加属性,然而这不是好的办法,原因在于对客观事物的描述不正确,因为属性是每个实例(也就是对象)都有一份,而方法应该不需要,方法是对数据进行处理,他是公用的,所以,更好的解决办法采用prototype原型方式。下面是改进的代码:

01 User.prototype.downloadResource=function(){ 

02          return "User类:下载资源"; 

03      }; 

04      //添加public 方法 

05      /*分享资源*/ 

06      User.prototype.shareResource=function(user){ 

07          this._score+=5; 

08          return "User类:分享资源"; 

09      }; 

10      //添加public方法 

11      /*显示用户信息*/ 

12      User.prototype.showInfo=function(){ 

13          var str_tmp=""; 

14          str_tmp+="userid:"+this._userid+"<br/>"; 

15          str_tmp+="name:"+this._name+"<br/>"; 

16          str_tmp+="age:"+this._age+"<br/>"; 

17          str_tmp+="score:"+this._score+"<br/>"; 

18          return str_tmp; 

19      }; 

类已经通过function关键字实现了,这是一种实现方式,这种方式实现起来简单、通俗易懂,其实还有一种实现方式,实现起来非常优雅,我们看下面代码:

01 var Class={ 

02          create:function(){ 

03              return function(){ 

04                  this.inital.apply(this,arguments); 

05              }; 

06          } 

07      }; 

08      /*定义User类*/ 

09      var User=Class.create(); 

10      win[´OOP´][´User´]=User; 

11      User.prototype={ 

12          inital:function(userid,uname,uage,uscore){ 

13              this._userid=userid; 

14              this._name=uname; 

15              this._age=uage; 

16              this._score=uscore; 

17          }, 

18          downloadResource:function(){ 

19              return "User类:下载资源"; 

20          }, 

21          shareResource:function(){ 

22              this._score+=5; 

23              return "User类:分享资源"; 

24          }, 

25          showInfo:function(){ 

26                  var str_tmp=""; 

27                  str_tmp+="userid:"+this._userid+"<br/>"; 

28                  str_tmp+="name:"+this._name+"<br/>"; 

29                  str_tmp+="age:"+this._age+"<br/>"; 

30                  str_tmp+="score:"+this._score+"<br/>"; 

31                  return str_tmp; 

32          } 

33      }; 

    关键代码在于:this.inital.apply(this,arguments);这样一行,Class本身是一个对象,他有一个方法叫做create,此方法返回一个函数指针,其中函数体内执行this.inital.apply(this,arguments);this指向是的是当前对象,在这里就是User,apply方法是更改initial方法的作用域,arguments是参数列表。也许有人还是迷迷糊糊的,那暂且先记住当new一个对象的时候,initial方法就会执行。类已经定义好了,我们可以像java、c#一样去实例化对象。

1 var user =new User(´10000´,´david´,24,1000); 

2 user.downloadResource(); 

    使用起来很方便,只能new一次初始化,然后调用相应的方法即可。

  第二步,实现继承。

  要解决两个问题:1.如何拷贝基类方法?2.属性如何继承?

  对于问题1,一般采用prototype原型方式,通过冒充对象。对于问题2,一般采用调用基类构造函数。现在我们来实现一个Member类,Member类继承User类,先采用第一种类定义的方式实现。代码如下:

01 //定义会员类 

02      function Member(userid,uname,uage,uscore,offlineSpace,level){ 

03          this._offlineSpace=offlineSpace; 

04          this._level=level; 

05          /*初始化基类参数*/ 

06          User.call(this,userid,uname,uage,uscore); 

07      } 

08      /*更改Member的prototype属性,以实现继承*/ 

09      Member.prototype=new User(); 

10      //添加public 方法 

11      //会员类有权限使用云点播 

12      Member.prototype.cloudPlay=function(){ 

13          return "Member类:云点播"; 

14      }; 

15      //添加public 方法 

16      //会员类有权限使用离线下载 

17      Member.prototype.offlineDownload=function(){ 

18          return "Member类:离线下载"; 

19      }; 

20      /*分享资源方法,假设计算积分方式不一样*/ 

21      Member.prototype.shareResource=function(){ 

22          this._score+=10; 

23          return "Member类:分享资源"; 

24      }; 

25      /*显示用户信息方法*/ 

26      Member.prototype.showInfo=function(){ 

27          var str_tmp=""; 

28          str_tmp+="userid:"+this._userid+"<br/>"; 

29          str_tmp+="name:"+this._name+"<br/>"; 

30          str_tmp+="age:"+this._age+"<br/>"; 

31          str_tmp+="score:"+this._score+"<br/>"; 

32          str_tmp+="offline Space:"+this._offlineSpace+"<br/>"; 

33          str_tmp+="level:"+this._level+"<br/>"; 

34          return str_tmp; 

35      }; 

 需要解释的地方有两处:

  1.调用基类构造函数代码

1 User.call(this,userid,uname,uage,uscore); 

  此行代码的含义是执行User类的构造函数,并且将Member的this指针域传递给User类,此时User类运行构造函数时,this指向的是Member对象。

  2.拷贝基类方法代码

1 Member.prototype=new User(); 

  此行代码的含义是new 一个User对象,将此引用传递给Member的原型,这样完成了基类方法的拷贝工作。(备注:此处代码一定要先执行,然后再添加新的方法)。

  第三步,实现多态。

  其实,在第二步已经将多态实现了。javascript函数被多次定义,最后面定义的会覆盖前面的定义,利用这个性质可以实现多态。代码如下:

01 //相当于override abstract(或virtual)方法 

02  /*重写基类分享资源方法,假设计算积分方式不一样*/ 

03  Member.prototype.shareResource=function(){ 

04      this._score+=10; 

05      return "Member类:分享资源"; 

06  }; 

07  //相当于override abstract(或virtual)方法 

08  /*重写显示用户信息方法*/ 

09  Member.prototype.showInfo=function(){ 

10      var str_tmp=""; 

11      str_tmp+="userid:"+this._userid+"<br/>"; 

12      str_tmp+="name:"+this._name+"<br/>"; 

13      str_tmp+="age:"+this._age+"<br/>"; 

14      str_tmp+="score:"+this._score+"<br/>"; 

15      str_tmp+="offline Space:"+this._offlineSpace+"<br/>"; 

16      str_tmp+="level:"+this._level+"<br/>"; 

17      return str_tmp; 

18  }; 

讲到这里,类、继承、多态都已经讲完了。原理应该都理解了,下面讲下之前说的那种优雅的方式定义类,那么如何利用它实现继承、多态呢?其实原理是一样的啦,只是代码会有点不同而已。代码如下:

01 /*定义Member类*/ 

02      var Member=Class.create(); 

03      win[´OOP´][´Member´]=Member; 

04      var repeater=new User(); 

05      Object.prototype.extend=function(obj){ 

06          for(var item in obj){ 

07              this[item]=obj[item]; 

08          } 

09      }; 

10      var fn={ 

11          inital:function(userid,uname,uage,uscore,offlineSpace,level){ 

12                      this._offlineSpace=offlineSpace; 

13                      this._level=level; 

14                      /*初始化基类参数*/ 

15                      User.prototype.inital.call(this,userid,uname,uage,uscore); 

16          }, 

17          cloudPlay:function(){ 

18              return "Member类:云点播"; 

19          }, 

20          offlineDownload:function(){ 

21              return "Member类:离线下载"; 

22          }, 

23          shareResource:function(){ 

24              this._score+=10; 

25              return "Member类:分享资源"; 

26          }, 

27          showInfo:function(){ 

28              var str_tmp=""; 

29              str_tmp+="userid:"+this._userid+"<br/>"; 

30              str_tmp+="name:"+this._name+"<br/>"; 

31              str_tmp+="age:"+this._age+"<br/>"; 

32              str_tmp+="score:"+this._score+"<br/>"; 

33              str_tmp+="offline Space:"+this._offlineSpace+"<br/>"; 

34              str_tmp+="level:"+this._level+"<br/>"; 

35              return str_tmp; 

36          } 

37      }; 

38      repeater.extend(fn); 

39      Member.prototype=repeater; 

同样要解决前面提到的两个问题:1.基类方法拷贝?2.属性如何继承?

  实现办法和第一种定义方式不太一样,首先看基类方法拷贝?现在有两个对象需要引用到Member的prototype原型上,为了达成此目的,扩展了Object对象方法,代码如下:

1 Object.prototype.extend=function(obj){ 

2          for(var item in obj){ 

3              this[item]=obj[item]; 

4          } 

5      }; 

  简单的说就是将两个对象合成一个,然后再赋给Member类的prototype原型引用。

  其次,继承基类属性。

1 User.prototype.inital.call(this,userid,uname,uage,uscore); 

         和第一种方式实现原理一样,只是现在的构造函数是initial。

  好了,讲到这里算是讲完了,有不正确的地方欢迎大家指点。

  第一种方式完整代码:

001 /* 

002 * OOP javascript demo 

003 * namespace OOP 

004 * developing tools UltraEdit 

005 * author 李检全 

006 * email lijianquan07@gmail.com 

007 * qq 55643287 

008 * date 2012-02-05 

009 */ 

010 (function(win){ 

011     if(!win[´OOP´]) 

012         win[´OOP´]={}; 

013   

014     //申明User类,其中users为一个数组,数组中的数据是单个user的信息 

015     function User(userid,uname,uage,uscore){ 

016         this._userid=userid; 

017         this._name=uname; 

018         this._age=uage; 

019         this._score=uscore; 

020     } 

021     /*对User类添加引用*/ 

022     win[´OOP´][´User´]=User; 

023     //添加public 方法 

024     /*用户下载资源*/ 

025     User.prototype.downloadResource=function(){ 

026         return "User类:下载资源"; 

027     }; 

028     //添加public 方法 

029     /*分享资源(abstract或者virtual方法)*/ 

030     User.prototype.shareResource=function(user){ 

031         this._score+=5; 

032         return "User类:分享资源"; 

033     }; 

034     //添加public方法 

035     /*显示用户信息(abstract或者virtual方法)*/ 

036     User.prototype.showInfo=function(){ 

037         var str_tmp=""; 

038         str_tmp+="userid:"+this._userid+"<br/>"; 

039         str_tmp+="name:"+this._name+"<br/>"; 

040         str_tmp+="age:"+this._age+"<br/>"; 

041         str_tmp+="score:"+this._score+"<br/>"; 

042         return str_tmp; 

043     }; 

044     //定义会员类 

045     function Member(userid,uname,uage,uscore,offlineSpace,level){ 

046         this._offlineSpace=offlineSpace; 

047         this._level=level; 

048         /*初始化基类参数*/ 

049         User.call(this,userid,uname,uage,uscore); 

050     } 

051     win[´OOP´][´Member´]=Member; 

052     /*更改Member的prototype属性,以实现继承*/ 

053     Member.prototype=new User(); 

054     //添加public 方法 

055     //会员类有权限使用云点播 

056     Member.prototype.cloudPlay=function(){ 

057         return "Member类:云点播"; 

058     }; 

059     //添加public 方法 

060     //会员类有权限使用离线下载 

061     Member.prototype.offlineDownload=function(){ 

062         return "Member类:离线下载"; 

063     }; 

064     //相当于override abstract(或virtual)方法 

065     /*重写基类分享资源方法,假设计算积分方式不一样*/ 

066     Member.prototype.shareResource=function(){ 

067         this._score+=10; 

068         return "Member类:分享资源"; 

069     }; 

070     //相当于override abstract(或virtual)方法 

071     /*重写显示用户信息方法*/ 

072     Member.prototype.showInfo=function(){ 

073         var str_tmp=""; 

074         str_tmp+="userid:"+this._userid+"<br/>"; 

075         str_tmp+="name:"+this._name+"<br/>"; 

076         str_tmp+="age:"+this._age+"<br/>"; 

077         str_tmp+="score:"+this._score+"<br/>"; 

078         str_tmp+="offline Space:"+this._offlineSpace+"<br/>"; 

079         str_tmp+="level:"+this._level+"<br/>"; 

080         return str_tmp; 

081     }; 

082     /*使用场景函数*/ 

083     function main(){ 

084       var user=new User(´10000´,´david´,24,1000); 

085         var arr_tmp=new Array(); 

086         arr_tmp.push("clas User"); 

087         arr_tmp.push("==================================================="); 

088         arr_tmp.push(user.showInfo()); 

089         arr_tmp.push(user.downloadResource()); 

090         arr_tmp.push(user.shareResource()); 

091         arr_tmp.push("===================================================<br/><br/>"); 

092   

093         var member=new Member(´10001´,´beckham´,24,9000,"100Gb","十级"); 

094         arr_tmp.push("class Member"); 

095         arr_tmp.push("==================================================="); 

096         arr_tmp.push(member.showInfo()); 

097         arr_tmp.push(member.downloadResource()); 

098         arr_tmp.push(member.cloudPlay()); 

099         arr_tmp.push(member.offlineDownload()); 

100         arr_tmp.push(member.shareResource()); 

101         arr_tmp.push("==================================================="); 

102         var tar_obj=document.getElementById("wrap"); 

103         tar_obj.innerHTML=arr_tmp.join(´<br/>´); 

104     } 

105     win[´OOP´][´main´]=main; 

106     //执行结果 

107     window.οnlοad=function(){ 

108         main(); 

109     }; 

110   

111 })(window); 

第二种方式完整代码:

view source

001 /* 

002 * OOP javascript demo 

003 * namespace OOP 

004 * developing tools UltraEdit 

005 * author 李检全 

006 * email lijianquan07@gmail.com 

007 * qq 55643287 

008 * date 2012-02-05 

009 */ 

010 /* 

011 ============================================================ 

012     注释请看OOP1.src.js,注释就不重写了。 

013 ============================================================ 

014 */ 

015 (function(win){ 

016     if(!win[´OOP´]) 

017         win[´OOP´]={}; 

018     var Class={ 

019         create:function(){ 

020             return function(){ 

021                 this.inital.apply(this,arguments); 

022             }; 

023         } 

024     }; 

025     /*定义User类*/ 

026     var User=Class.create(); 

027     win[´OOP´][´User´]=User; 

028     User.prototype={ 

029         inital:function(userid,uname,uage,uscore){ 

030             this._userid=userid; 

031             this._name=uname; 

032             this._age=uage; 

033             this._score=uscore; 

034         }, 

035         downloadResource:function(){ 

036             return "User类:下载资源"; 

037         }, 

038         shareResource:function(){ 

039             this._score+=5; 

040             return "User类:分享资源"; 

041         }, 

042         showInfo:function(){ 

043                 var str_tmp=""; 

044                 str_tmp+="userid:"+this._userid+"<br/>"; 

045                 str_tmp+="name:"+this._name+"<br/>"; 

046                 str_tmp+="age:"+this._age+"<br/>"; 

047                 str_tmp+="score:"+this._score+"<br/>"; 

048                 return str_tmp; 

049         } 

050     }; 

051     /*定义Member类*/ 

052     var Member=Class.create(); 

053     win[´OOP´][´Member´]=Member; 

054     var repeater=new User(); 

055     Object.prototype.extend=function(obj){ 

056         for(var item in obj){ 

057             this[item]=obj[item]; 

058         } 

059     }; 

060     var fn={ 

061         inital:function(userid,uname,uage,uscore,offlineSpace,level){ 

062                     this._offlineSpace=offlineSpace; 

063                     this._level=level; 

064                     /*初始化基类参数*/ 

065                     User.prototype.inital.call(this,userid,uname,uage,uscore); 

066         }, 

067         cloudPlay:function(){ 

068             return "Member类:云点播"; 

069         }, 

070         offlineDownload:function(){ 

071             return "Member类:离线下载"; 

072         }, 

073         shareResource:function(){ 

074             this._score+=10; 

075             return "Member类:分享资源"; 

076         }, 

077         showInfo:function(){ 

078             var str_tmp=""; 

079             str_tmp+="userid:"+this._userid+"<br/>"; 

080             str_tmp+="name:"+this._name+"<br/>"; 

081             str_tmp+="age:"+this._age+"<br/>"; 

082             str_tmp+="score:"+this._score+"<br/>"; 

083             str_tmp+="offline Space:"+this._offlineSpace+"<br/>"; 

084             str_tmp+="level:"+this._level+"<br/>"; 

085             return str_tmp; 

086         } 

087     }; 

088     repeater.extend(fn); 

089     Member.prototype=repeater; 

090     function main(){ 

091       var user=new User(´10000´,´david´,24,1000); 

092         var arr_tmp=new Array(); 

093         arr_tmp.push("clas User"); 

094         arr_tmp.push("==================================================="); 

095         arr_tmp.push(user.showInfo()); 

096         arr_tmp.push(user.downloadResource()); 

097         arr_tmp.push(user.shareResource()); 

098         arr_tmp.push("===================================================<br/><br/>"); 

099   

100         var member=new Member(´10001´,´beckham´,24,9000,"100Gb","十级"); 

101         arr_tmp.push("class Member"); 

102         arr_tmp.push("==================================================="); 

103         arr_tmp.push(member.showInfo()); 

104         arr_tmp.push(member.downloadResource()); 

105         arr_tmp.push(member.cloudPlay()); 

106         arr_tmp.push(member.offlineDownload()); 

107         arr_tmp.push(member.shareResource()); 

108         arr_tmp.push("==================================================="); 

109         var tar_obj=document.getElementById("wrap"); 

110         tar_obj.innerHTML=arr_tmp.join(´<br/>´); 

111     } 

112     win[´OOP´][´main´]=main; 

113     //执行结果 

114     window.οnlοad=function(){ 

115         main(); 

116     }; 

117   

118 })(window); 

 js的几种数据类型:number, string, boolean, object, undefined 五种数据类型

        js的常见内置对象类:Date, Array, Math, Number, Boolean, String, RegExp, Function,Object。

        js的两个类型判断方法:typeof、instanceof

        typeof:一元运算符,eg: typeof(1) 将返回number.它返回的是顶级的几种数据类型

        instanceof:二元运算符 eg: myObj instanceOf MyObj 返回的是一个boolean值。应用数据类型包括自己定义的类。判断变量是不是此类的实例,它和typeof都只能应用于js内置及自定义的数据类型,不包括DOM对像,例如window,document.

        我们平台声明的数据类型也就是number,string,boolean,object undefined及其object的子类型。声明的类型也就拥有所属类型的本性方法,常用的有string类型的substring,indexOf,length方法。主要的是要掌握function 定义出的类型。

        我们利用function 可以声明一个函数,也可以做为创建类的关键字。当作为函数关键词使用时,没有什么好说的。但是当做为创建类的关键词时,我们需要知道js的一些特征东西。(使用function做为函数关键词还是类关键词,因自己的目的而定)。当我们使用函数关键词时js面向对象的特征基本用不到,所以就不多说了。现在就说一下function做为类关键词,我们需要常用的一些js特征,以适应我们的面向对象的

三大特性:封装、继承、多态

 

封装性:

         类型于其它的面向对象语言一样,用大括号来限定变量的作用域。这样就有了局部变量。如果声明在最外面,那就是全局变量。那么现在还少的是什么?就是成员变量。幸好js支持闭包特性,例如:

         function Person(){

            var name="张三";

            var sex ="男";

            return {

                   "getName":function(){

                            return name;

                   },

                   "getSex":function(){

                            return sex;

                   },

                   "setName":function(_name){

                            name=_name;

                   },

                   "setSex":function(_sex){

                            sex = _sex;

                   },

                   "aliasName":name;

            };        

         }

                   var person = new Person();

                   alert(person.name);//undefined

                   alert(person.getName());//张三

                   person.setName("李国");

                   alert(person.getName());//李国

         这样就有了私有成员变量。公有成员变量,就是可以直接访问的,像aliasName属性。这是很基础的。我们都知道,在像java语言里,有this关键字

         来代表对像本身。而恰恰js也有这个this.但是这个this和其它面向对向语言的this是有区别的,这里先不说区别在哪。

         我们先利用this实现一下类:

         function Person(){

                   var name="王五";

                   var sex ="男";

                   this.aliasName="123";

                   this.setName=function( _name){

                            name=_name;

                   };

                   this.getName=function(){

                            return name;

                   };

                   this.setSex=function( _sex){

                            sex=_sex;

                   };

                   this.getSex=function(){

                            return sex;

                   }

         }

         //测试

         var person = new Person();

         alert(person.name);//undefined

         alert(person.getName());//张三

         person.setName("李国");

         alert(person.getName());//李国

         person.aliasName="nnd";

         alert(person.aliasName);

         下面我们来看一下 person = new Person()执行过程:person=new Object()-->把Person类型的this绑定到this-->person有什么,person就是有什么。

         我们想一下,这里也是利用的闭包特性。因person的相关方法,引用了外部变量,当person不被回收时,外部变量也不会被回收,但是只能通过

         person的方法才能访问的到。被包住了。但是,这一种方式,要比第一个实现起来要灵活多了。这里就要说一下,this为什么和其它语言的this

         有区别,想一下,js 的function 即可以以函数用也可以做类用,当一个function 含有this,但被直接调用了,像Person().这时js就会把相关的方法

         属性给了window(this没有被指向一个对象,会默认指向window),执行过程:this-->window.这样的话,就有一定的凶险性了,因为这个类里的所有东西都是全局的了。比如:

                   Person();

                   alert(window.getName());

        

         到现在为止,我们把对像的封闭性创建对象算是说完了。目前,我们看它像是一个类的,但还有一点就是每new Person(),不但属性产生副本,方法也会产生副本

         属性产生副本,这个是应该的,但是方法产生副本就没有毕要了,对吧?js function类型的数据(切记),它提供了prototype这个属性,即原型。这么写Person:

         function Person(){

                   var name="王五";

                   var sex ="男";

                   this.aliasName="123";

                   this.self = Person;

                   this.self.prototype.setName=function( _name){

                            name=_name;

                   };

                   this.self.prototype.getName=function(){

                            return name;

                   };

                   this.self.prototype.setSex=function( _sex){

                            sex=_sex;

                   };

                   this.self.prototype.getSex=function(){

                            return sex;

                   }

         }

         创建的所有Person就会有一个方法副本了。为了证明正确性,可以分别:

          var person = new Person();

          var person1 = new Person();

          alert(person.getName===person1.getName);

          看一下效果就可以了。原型为什么能提供这个效果?

         下面,就说一下原型prototype及其相关应用吧

         原型是Js中非常重要的概念,每个函数(在Js里面函数也是对象)都有一个叫prototype即原型)的属性,不过在一般情况下它的值都是null,但它他有一项非常重要的功能就是所以实例都会共享它里面的属性和方法(这就是Js里面实现继承的基础)!

还是举例吧:

 

function auth(){

        alert(this.name);

        //此处一定要加this关键字

}

auth.prototype.name='shiran';//这句可以放到对象定义之后,但必须在被调用之前

new auth();//这里一定要用new

 

这里需要注意三点:

第一、name前面一定要加关键字this,不然的话就会得不到任何,因为如果你不加this,他就不会到原型中查找(加上this是属性它会在上下文查找,看看前面我讲的变量的查找那一章)。

第二、如果实例对象在对象中找不到属性或方法就会到对象的prototype中去查找,所以如果想在函数被调用的时候调用对象的属性或方法,就必须把调用语句放在prototype定义之后(在这里,就是new auth必须放到auth.prototype.name之后)!

第三、只有实例对象才会到原型中查找,因为对于原对象来说prototype是他的属性必需通过prototype才能访问(在这里,要用new auth()生成一个实例,而不能用auth)!

 

原型对于对象的实例来说是共享的,这既给程序带来方便,同时也会让人感到迷惑,出现好多让人意想不到的结果!

auth=function(){ };

auth.prototype={

name:[],

getNameLen:function(){

        alert(this.name.length);

},

setName:function(n){

        this.name.push(n);

}

}

 

var lwx=new auth();

lwx.setName('lwx');

lwx.getNameLen();

var shiran=new auth();

shiran.setName('shiran');

shiran.getNameLen();

 

第二次弹出的对话框显示name的长度已经是2,为什么呢?这就是原型的共享引起的,因为变量lwx和shiran都是auth对象,而且name属性是在auth对象的原型中定义的,所以lwx和shiran实例之间共享name这个属性!!可是这并不是我们想要看到的结果,因为我们希望,每个实例之间是相互隔离的。

这里我们可以把name属性从原型中去掉,放在auth对象的定义中即:

 

auth=function(){

this.name=[];//切记,一定要在前面加上this关键词

};

        这样一来,每个auth的实例都会拥有自己的name属性!所以推荐大家,以后在定义对象的时候:把属性放到定义里,而把对象的方法放到原型里!

 

        有了原型,也其实就有了类方法和类属性。完整的类有了,封装成员也有了。以后要说一下继承啦!模拟继承,我们需要熟悉js中function 的两个函数,它们是call及apply.下面,我们介绍一下它们。知道它们了,继承也就差不多了,写信demo就可以了。

 

        JavaScript中有一个call和apply方法,其作用基本相同,但也有略微的区别。先来看看JS手册中对call的解释:

 

call 方法

调用一个对象的一个方法,以另一个对象替换当前对象。

 

call([thisObj[,arg1[, arg2[,   [,.argN]]]]])

 

参数

thisObj

可选项。将被用作当前对象的对象。

arg1, arg2,  , argN

可选项。将被传递方法参数序列。

说明

call 方法可以用来代替另一个对象调用一个方法。call 方法可将一个函数的对象上下文从初始的上下文改变为由thisObj 指定的新对象。如果没有提供 thisObj 参数,那么 Global 对象被用作 thisObj。说明白一点其实就是更改对象的内部指针,即改变对象的this指向的内容。这在面向对象的js编程过程中有时是很有用的。

 

引用网上一个代码段,运行后自然就明白其道理。

 

Js代码 

<input type="text" id="myText"   value="input text">  

<script>  

   function Obj(){this.value="对象!";}   

   var value="global 变量";  

   function Fun1(){alert(this.value);}  

 

   window.Fun1();   //global 变量  

   Fun1.call(window);  //global 变量  

   Fun1.call(document.getElementById('myText'));  //input text  

   Fun1.call(new Obj());   //对象!   </script> 

<input type="text" id="myText"   value="input text">

<script>

    function Obj(){this.value="对象!";}

    var value="global 变量";

    function Fun1(){alert(this.value);}

 

    window.Fun1();   //global 变量

    Fun1.call(window);  //global 变量

    Fun1.call(document.getElementById('myText'));  //input text

    Fun1.call(new Obj());   //对象!

</script>

       call函数和apply方法的第一个参数都是要传入给当前对象的对象,及函数内部的this。后面的参数都是传递给当前对象的参数。

       运行如下代码:

Js代码 

<script>  

   var func=new function(){this.a="func"}  

    var myfunc=function(x){  

        var a="myfunc";  

       alert(this.a);  

       alert(x);  

   }  

   myfunc.call(func,"var");  

</script> 

<script>

   var func=new function(){this.a="func"}

    var myfunc=function(x){

        var a="myfunc";

        alert(this.a);

        alert(x);

    }

    myfunc.call(func,"var");

</script> 可见分别弹出了func和var。到这里就对call的每个参数的意义有所了解了。

 

        对于apply和call两者在作用上是相同的,但两者在参数上有区别的。对于第一个参数意义都一样,但对第二个参数:apply传入的是一个参数数组,也就是将多个参数组合成为一个数组传入,而call则作为call的参数传入(从第二个参数开始)。如func.call(func1,var1,var2,var3)对应的apply写法为:func.apply(func1,[var1,var2,var3])

 

        同时使用apply的好处是可以直接将当前函数的arguments对象作为apply的第二个参数传入。我们用call,写一个继承的例子,像:

    function Animal(){

         this.name=“gg”;

         this.getName=function(){alert(this.name)};

    };

    function Person(){

         Animal.call(this);//构造父类

         this.aliasName="";

    }

 

   function init(){

         var p = new Person();

         p.getName();

   }

   再写一个apply函数的:

    function Animal(name,sex){

                   this.name="gg";

                   this.getName=function(){alert(this.name+name+sex)};

    };

    function Person(name,sex){

                   Animal.apply(this,[name,sex]);//构造父类,参数是数组

                   this.aliasName="";

    }

 

   function init(){

                   var p = new Person("wsk",'man');

                   p.getName();

   }

到这里,继承有了。

 

 多态性:

      多态性,对于js来讲更简单了,因为,它是一个弱类型的语言,可以对一个变量付任何类型的变量。虽然不完全符合,但也能凑合用了。

js 面向对象编程三大特征: 封装:把抽象出的属性和对属性的操作封装在一起,属性被保护在内部,程序的其它部分只 有通过被授权的操作(函数) ,才能对属性进行操作。 继承:冒充方法 function Animal(name){ this.name=name; this.show=function(){ window.alert("父类:"+this.name); } } function Dog(name){ this.a=Animal; this.a(name);//这句不能少 this.show=function(){ //覆盖掉父类的方法了 window.alert("子类:"+this.name); } } var dog=new Dog("大黄"); dog.show(); //结果:子类:大黄 function Cat(name){ this.a=Animal; this.a(name); } var cat=new Cat("小红"); cat.show(); //结果:父类:小红 注:可实现多重继承。 js 的重载和重写: js 不支持重载, 不可以通过参数的个数来确定调用哪个函数。 但是 js 本身支持可变参数, 因此可认为 js 天然支持重载。 重写:子类可以重新写函数来覆盖父类的同名函数。 多态: js 实际上是无态的, 是一种多态语言, 一个变量的类型是在运行的过程中由 js 引擎来决 定的,所以 js 天生就支持多态。

闭包

闭包是和 gc 相关联的。 闭包实际上是涉及到一个对象的属性,何时被 gc 处理的问题。 怎样才能对对象的属性形成一个闭包。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值