最新公告
  • 欢迎您光临码农资源网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!加入我们
  • 学习JavaScript设计模式(封装)

    在JavaScript 中,并没有对抽象类和接口的支持。JavaScript 本身也是一门弱类型语言。在封装类型方面,JavaScript 没有能力,也没有必要做得更多。对于JavaScript 的设计模式实现来说,不区分类型是一种失色,也可以说是一种解脱。

    从设计模式的角度出发,封装在更重要的层面体现为封装变化。

    通过封装变化的方式,把系统中稳定不变的部分和容易变化的部分隔离开来,在系统的演变过程中,我们只需要替换那些容易变化的部分,如果这些部分是已经封装好的,替换起来也相对容易。这可以最大程度地保证程序的稳定性和可扩展性。

    javascript封装的的基本模式有3种:

    1、使用约定优先的原则,将所有的私有变量以_开头

     <script type="text/javascript">
      /**
       * 使用约定优先的原则,把所有的私有变量都使用_开头
       */
      var Person = function (no, name, age)
      {
       this.setNo(no);
       this.setName(name);
       this.setAge(age);
      }
      Person.prototype = {
       constructor: Person,
       checkNo: function (no)
       {
        if (!no.constructor == "string" || no.length != 4)
         throw new Error("学号必须为4位");
       },
       setNo: function (no)
       {
        this.checkNo(no);
        this._no = no;
       }, 
       getNo: function ()
       {
        return this._no;
       setName: function (name)
       {
        this._name = name;
       }, 
       getName: function ()
       {
        return this._name;
       }, 
       setAge: function (age)
       {
        this._age = age;
       }, 
       getAge: function ()
       {
        return this._age;
       }, 
       toString: function ()
       {
        return "no = " + this._no + " , name = " + this._name + " , age = " + this._age;
       }
      };
      var p1 = new Person("0001", "小平果", "22");
      console.log(p1.toString());  //no = 0001 , name = 小平果 , age = 22
      p1.setNo("0003");
      console.log(p1.toString());  //no = 0003 , name = 小平果 , age = 22
      p1.no = "0004";
      p1._no = "0004";
      console.log(p1.toString()); //no = 0004 , name =小平果 , age = 22
    
     </script>
    

    看完代码,是不是有种被坑的感觉,仅仅把所有的变量以_开头,其实还是可以直接访问的,这能叫封装么,当然了,说了是约定优先嘛。

    下划线的这种用法这一个众所周知的命名规范,它表明一个属性仅供对象内部使用,直接访问它或设置它可能会导致意想不到的后果。这有助于防止程序员对它的无意使用,却不能防止对它的有意使用。

    这种方式还是不错的,最起码成员变量的getter,setter方法都是prototype中,并非存在对象中,总体来说还是个不错的选择。如果你觉得,这不行,必须严格实现封装,那么看第二种方式。

    2、严格实现封装

    <script type="text/javascript">
      /**
       * 使用这种方式虽然可以严格实现封装,但是带来的问题是get和set方法都不能存储在prototype中,都是存储在对象中的
       * 这样无形中就增加了开销
       */
      var Person = function (no, name, age)
      {
       var _no , _name, _age ;
       var checkNo = function (no)
       {
        if (!no.constructor == "string" || no.length != 4)
         throw new Error("学号必须为4位");
       };
       this.setNo = function (no)
       {
        checkNo(no);
        _no = no;
       };
       this.getNo = function ()
       {
        return _no;
       }
       this.setName = function (name)
       {
        _name = name;
       }
    
       this.getName = function ()
       {
        return _name;
       }
    
       this.setAge = function (age)
       {
        _age = age;
       }
       this.
         getAge = function ()
       {
        return _age;
       }
    
       this.setNo(no);
       this.setName(name);
       this.setAge(age);
      }
      Person.prototype = {
       constructor: Person,
       toString: function ()
       {
        return "no = " + this.getNo() + " , name = " + this.getName() + " , age = " + this.getAge();
       }
      }
      ;
      var p1 = new Person("0001", "小平果", "22");
      console.log(p1.toString());  //no = 0001 , name =小平果 , age = 22
      p1.setNo("0003");
      console.log(p1.toString());  //no = 0003 , name = 小平果 , age = 22
      p1.no = "0004";
      console.log(p1.toString()); //no = 0003 , name = 小平果 , age = 22
    
     </script>
    
    

    那么这与我们先前讲过的其他创建对象的模式有什么不同呢,在上面的例子中,我们在创建和引用对象的属性时总要使用this关键字。而在本例中,我们用var声明这些变量。这意味着它们只存在于Person构造器中。checkno函数也是用同样的方式声明的,因此成了一个私用方法。

    需要访问这些变量和函数的方法只需要声明在Person中即可。这些方法被称为特权方法,因为它们是公用方法,但却能够访问私用属性和方法。为了在对象外部能访问这些特权函数,它们的前面被加上了关键字this。因为这些方法定义于Person构造器的作用域,所以它们能访问到私用属性。引用这些属性时并没有使用this关键字,因为它们不是公开的。所有取值器和赋值器方法都被改为不加this地直接引用这些属性。

    任何不需要直接访问的私用属性的方法都可以像原来那样在Person.prototype中声明。像toString()方法。只有那些需要直接访问私用成员的方法才应该被设计为特权方法。但特权方法太多又会占用过多的内存,因为每个对象实例都包含所有特权方法的新副本。

    看上面的代码,去掉了this.属性名,严格的实现了封装,只能通过getter,setter访问成员变量了,但是存在一个问题,所有的方法都存在对象中,增加了内存的开销。

    3、以闭包的方式封装

    <script type="text/javascript">
    
      var Person = (function ()
      {
       //静态方法(共享方法)
       var checkNo = function (no)
       {
        if (!no.constructor == "string" || no.length != 4)
         throw new Error("学号必须为4位");
       };
       //静态变量(共享变量)
       var times = 0;
    
        //return the constructor.
       return function (no, name, age)
       {
        console.log(times++); // 0 ,1 , 2
        var no , name , age; //私有变量
        this.setNo = function (no) //私有方法
        {
         checkNo(no);
         this._no = no;
        };
        this.getNo = function ()
        {
         return this._no;
        }
        this.setName = function (name)
        {
         this._name = name;
        }
    
        this.getName = function ()
        {
         return this._name;
        }
    
        this.setAge = function (age)
        {
         this._age = age;
        }
        this.getAge = function ()
        {
         return this._age;
        }
    
        this.setNo(no);
        this.setName(name);
        this.setAge(age);
       }
      })();
    
      Person.prototype = {
       constructor: Person,
       toString: function ()
       {
        return "no = " + this._no + " , name = " + this._name + " , age = " + this._age;
       }
      };
    
      var p1 = new Person("0001", "小平果", "22");
      var p2 = new Person("0002", "abc", "23");
      var p3 = new Person("0003", "aobama", "24");
    
    
      console.log(p1.toString());  //no = 0001 , name = 小平果 , age = 22
      console.log(p2.toString());  //no = 0002 , name = abc , age = 23
      console.log(p3.toString()); //no = 0003 , name = aobama , age = 24
    
     </script>
    
    

    上述代码,js引擎加载完后,会直接执行Person = 立即执行函数,然后此函数返回了一个子函数,这个子函数才是new Person所调用的构造函数,又因为子函数中保持了对立即执行函数中checkNo(no) ,times的引用,(很明显的闭包)所以对于checkNo和times,是所有Person对象所共有的,创建3个对象后,times分别为0,1,2 。这种方式的好处是,可以使Person中需要复用的方法和属性做到私有且对象间共享。

    这里的私用成员特权成员仍然被声明在构造器。但那个构造器却从原来的普通函数变成了一个内嵌函数,并且被作为包含它的函数的返回值给变量Person。这就创建了一个闭包,你可以把静态的私用成员声明在里面。位于外层函数声明之后的一对空括号很重要,其作用是代码一载入就立即执行这个函数。这个函数的返回值是另一个函数,它被赋给Person变量,Person因此成了一个构造函数。在实例华Person时,所调用的这个内层函数。外层那个函数只是用于创建一个可以用来存储静态成员的闭包。

    在本例中,checkno被设计成为静态方法,原因是为Person的每个实例都生成这个方法的一个新副本毫无道理。此外还有一个静态属性times,其作用在于跟踪Person构造器的总调用次数

    以上就是本文的全部内容,希望对大家的学习有所帮助,大家可以更深入的学习了解封装的意义。

    想要了解更多内容,请持续关注码农资源网,一起探索发现编程世界的无限可能!
    本站部分资源来源于网络,仅限用于学习和研究目的,请勿用于其他用途。
    如有侵权请发送邮件至1943759704@qq.com删除

    码农资源网 » 学习JavaScript设计模式(封装)
    • 7会员总数(位)
    • 25846资源总数(个)
    • 0本周发布(个)
    • 0 今日发布(个)
    • 293稳定运行(天)

    提供最优质的资源集合

    立即查看 了解详情