JavaScript实现类的private、protected、public、static以及继承 您所在的位置:网站首页 js中的类和继承 JavaScript实现类的private、protected、public、static以及继承

JavaScript实现类的private、protected、public、static以及继承

2024-07-03 05:30| 来源: 网络整理| 查看: 265

原文出处:  Yorhom’s Game Box    基础知识 JavaScript中的类

JavaScript实际上是一种弱类型语言,与C++和Java等语言不同。因此,在JavaScript中,没有强调类(class)这一概念,但实际运用中,类还是很重要的,比如写一款游戏,如果我们不停地调用函数来完成创建角色,移动角色的话,那会是什么样的呢?可能会出现非常多的重复代码,因此我们需要一个类来统一这些代码。所谓的类,就是把程序中的代码分类,比如说游戏中的关于角色的代码算作一类,游戏背景算作一类,游戏特效又是一类。这样一来,我们对类进行操作,就不会使代码显得很凌乱,冗杂。虽然Js是弱类型语言,但是也提供了类这一概率。定义Js中的类,实际上用的是function,总所周知,这个语法其实是用来定义函数的。不用于定义函数的是,我们可以在function中通过this.xxx的方式来定义属性和方法。比如说:

JavaScript 1 2 3 4 5 6 7 function People ( ) {      this . name = "Yorhom" ;        this . getName = function ( ) {          return this . name      } ; }

使用的时候使用new:

JavaScript 1 2 3 var yorhom = new People ( ) ; // "Yorhom" alert ( yorhom . getName ( ) ) ;

可以看到,这样就可以使用到我们定义的类和类中的方法了。也许你会问this.xxx只能定义公有属性和方法,那私有属性和方法怎么办呢?这个可以用到js闭包的知识来解决:

JavaScript 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 function People ( ) {      this . name = "Yorhom" ;        var age = 16 ;        this . getName = function ( ) {          return this . name      } ;        this . getAge = function ( ) {          return age ;      } ; }   var yorhom = new People ( ) ; // undefined alert ( yorhom . age ) ; // 16 alert ( yorhom . getAge ( ) ) ;

 

可以看到,这里的age就是一个私有属性了。

JavaScript中的prototype

上面的代码美中不足的地方就是,如果一个类有很多方法,同时用到这个类的地方又有很多(也就是new出来的对象有很多),那么用上面的代码就会出现内存占用过剩的问题。问题的根本原因在于,每次实例化一个对象,这个类就会执行构造器里的代码(以People类为例就是function People () {…}执行的代码),因此每当这个类被实例化的时候,这些方法和属性就会被拷贝到实例化出来的对象中。这样一来,就会造成“吃”内存的现象。于是js中的prototype就诞生了。prototype的作用通常是给一个类添加一系列常量或者方法。 每当一个类被实例化之后,实例化出来的对象会自动获取类的prototype中定义的方法和属性。只不过这里的获取类似于C++里面的引用,不会在内存里对这些方法和属性进行复制,而是指向这些方法和属性。示例:

JavaScript 1 2 3 4 5 6 7 8 9 10 11 function People ( ) {      this . name = "Yorhom" ; }   People . prototype . getName = function ( ) {      return this . name ; } ;   var yorhom = new People ( ) ; // "Yorhom" alert ( yorhom . getName ( ) ) ;

 

这种方法虽然可以节约内存,但是,美中不足的是,无法定义私有属性。

类的继承

Javascript没有提供继承的函数,所以只有自己写了。这里借用lufylegend.js中的继承方法向大家展示如何实现继承:

JavaScript 1 2 3 4 5 6 7 8 9 10 11 12 13 14 function base ( d , b , a ) {      var p = null , o = d . constructor . prototype , h = { } ;        for ( p in o ) {          h [ p ] = 1 ;      }      for ( p in b . prototype ) {          if ( ! h [ p ] ) {              o [ p ] = b . prototype [ p ] ;          }      }        b . apply ( d , a ) ; }

这里的base就是继承函数了。继承函数的原理莫过于复制类的方法和属性。因此,只要做到这点,就可以实现类的继承了。可以在上面的代码中看见,我们通过遍历prototype来获取原型链中定义的方法和属性。通过apply调用父类的构造器进行构造器中属性和方法的复制。使用示例:

JavaScript 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 function People ( ) {      this . name = "Yorhom" ; }   People . prototype . getName = function ( ) {      return this . name ; } ;   function Student ( ) {      base ( this , People , [ ] ) ; }   var yorhom = new Student ( ) ; // "Yorhom" alert ( yorhom . getName ( ) ) ;

 

静态属性和方法的定义

静态属性和方法以及静态类在js中的定义非常简单,先来看静态类:

JavaScript 1 var StaticClass = { } ;

这么写不是在定义一个Object吗?是的,不错,不过js中的静态类也是可以这样定义的。如果要添加静态类中的方法和属性,就可以这么写:

JavaScript 1 2 3 4 5 6 var StaticClass = {      id : 5 ,      sayHello : function ( ) {          alert ( "Hello" ) ;      } } ;

如果是要向类中添加静态属性或者方法,可以采用这种写法:

JavaScript 1 2 3 4 5 6 7 8 9 10 11 12 function People ( ) {      this . name = "Yorhom" ; }   People . prototype . getName = function ( ) {      return this . name ; } ;   People . TYPE = "people" ; People . sayHello = function ( ) {      alert ( "Hello" ) ; } ;

 

实现一个功能丰富的类

我们在上文中提到了,节省内存和定义私有属性两者无法兼得,是啊,和“鱼和熊掌不可兼得”是一个道理,在通常的使用过程中,我们需要对这两项进行取舍。但是现在这个年代,哪有不可兼得的呢?鱼和熊掌不能同时吃?当然不行……因为吃熊掌是违法的(有待考证)?不过至少鸡和鱼是可以同时吃的吧。由于js没有实现私有属性的定义,所以这其实是一个没有头绪的工作,因为在标准的做法中,我们除了闭包可以阻止外部访问,没有别的办法了。所以这里我们要用点歪门邪道的方法了。

JavaScript Set/Get访问器

什么是set/get访问器呢?如果你熟悉python,那么你可以理解为@property和@xxx.setter,但是简陋的js里也有?当然有,只不过是ES5的标准,可以采用这种写法:

JavaScript 1 2 3 4 5 6 7 8 9 Object . defineProperty ( this , "name" , {      get : funtion ( ) {          return name ;      } ,        set : function ( v ) {          name = v ;      } } ) ;

 

具体有什么用呢?大致就是this.name属性在被获取的时候调用get访问器,在被更改值的时候调用set。你可以从上面的代码了解大致的写法,不过如果你想深究,可以参考这篇文章:http://blog.csdn.net/teajs/article/details/22738851

注意以上的这种用法会有兼容性问题,浏览器支持情况如下:

PC端

FirefoxGoogle ChromeInternet ExplorerOperaSafari4.05911.65.1

移动端

Firefox MobileAndroidIE MobileOpera MobileSafari Mobile4.0Yes911.5Yes

来自: https://developer.mozilla.org/…/defineProperty#Browser_compatibility

如何“歪门邪道”地做到禁止访问私有和保护属性?

这是个比较头疼的问题,正如本节开篇所说,我们在常规开发下,只能通过闭包来阻止某变量的访问。可是如果你使用了prototype,那么闭包这条路就走不通了。在这种情况下,我们的Object.defineProperty就出场了。我们知道,通过这个函数可以设定获取属性时返回的值,也可以设定更改属性时设置的值。有了这个函数,我们可以随时跟踪到某个属性是不是在被获取,或者是不是在被更改。我们还需要一个开关,我们在类内部的方法调用时,把这个开关打开,表明是在内部运行,方法调用结束后将开关关闭,表明回到外部运行状态。有了这两个状态,我们就可以跟踪private和protected属性和方法了,一旦他们在开关关闭的时候被使用,就终止这个属性或方法的获取或设置。于是乎,大难题就快解决了。

开源库件jpp.js

秉着这个歪门邪道的思想,我把这个功能封装到jpp.js这个库件中,库件的github地址如下:https://github.com/yuehaowang/jpp.js当然这个库件不限于创建一个类,还可以实现函数的重载等。目前库件还处于开发阶段,欢迎各位提交建议。

使用jpp.js创建一个类

JavaScript 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 var People = jpp . class ( {      extends : null ,      private : {          id : null ,          hobby : null      } ,      protected : {          money : null ,          phoneNumber : null      } ,      public : {          firstName : null ,          lastName : null ,          age : null ,          birthday : null ,          occupation : null ,            constructor : function ( name , id ) {              if ( name ) {                  var nameArray = name . split ( " " ) ;                    this . firstName = nameArray [ 0 ] ;                  this . lastName = nameArray [ 1 ] ;              }                if ( id ) {                  this . id = id ;              }          } ,            setBirthday : function ( date ) {              if ( date ) {                  this . birthday = date ;              }          } ,            getBirthday : function ( ) {              return this . birthday ;          } ,            askForId : function ( ) {              return this . id ;          } ,            findHobby : function ( ) {              return this . hobby ;          }      } ,      static : {          OCCUPATION_PROGRAMMER : "programmer" ,          OCCUPATION_ARTIST : "artist" ,          OCCUPATION_MUSICIAN : "musician" ,          OCCUPATION_STUDENT : "student"      } } ) ;   var peter = new People ( "Peter Wong" , 543232123565 ) ; peter . occupation = People . OCCUPATION_PROGRAMMER ;   peter . setBirthday ( "19980727" ) ;   // result: Peter alert ( peter . firstName ) ; // result: 19990727 alert ( peter . getBirthday ( ) ) ; // result: 51092028 alert ( peter . askForId ( ) ) ; // result: null alert ( peter . findHobby ( ) ) ; // result: programmer alert ( peter . occupation ) ; // error alert ( peter . id ) ;

 

对上面的代码进行分析:使用jpp.class函数创建一个类,函数的参数是一个Object,这个Object可添加的属性如下:

extends 继承时的父类private 装载私有属性,里面定义的成员外部不可使用且不能继承给子类protected 装载保护属性,里面定义的成员外部不可使用但可以继承给子类public 装载公有属性static 装载静态方法和属性

在创建类的过程中,在public中添加constructor方法初始化构造器,this.super可访问父类构造器。

运行代码,可以看到浏览器正常运行前5个alert,而最后一个运行的时候浏览器报错:

具体的实现过程有点复杂,不过原理在上文已经详细讲述了。代码可以在github里参看,欢迎各位研究。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有